[Bug] Exit cleanup recursively deletes $HOME instead of session directory

Status Open
Reported on v2.1.238
Maintainer reply None cached
Activity 0 comments · opened Aug 21, 2026

[Data loss] Entire $HOME recursively deleted at /exit of a session managing background agents in worktrees

Summary

On Claude Code v2.1.238 (Linux, Ubuntu, native installer), typing /exit in an
interactive session that had spawned two background agent sessions running in
managed worktrees
(<project>/.claude/worktrees/<name>) coincided, within
seconds, with a recursive deletion of the user's entire home directory
(~670,000 inodes freed). Forensic reconstruction of the disk shows no shell
deletion command was executed by any session — the deletion pattern is
consistent with an in-process recursive remove (e.g. fs.rm-style) during
exit-time cleanup resolving to $HOME instead of the intended worktree/session
directory.

Environment

  • Claude Code 2.1.238, native install (~/.local/bin/claude

~/.local/share/claude/versions/2.1.238)

  • Ubuntu (GNOME, Wayland), ext4 root filesystem, single user (uid 1000)
  • No custom hooks: forensic search of the disk image found **no

WorktreeRemove/WorktreeCreate hook configuration** in any settings file —
only the strings inside the Claude Code binary itself

  • Permission mode of the agent sessions: auto; sessions of kind bg

Setup at the time of the incident

  • Session A (interactive, "orchestrator"): started ~12:15 local in the main

project directory; spawned/managed background agent sessions

  • Sessions B and C (background agents): started ~09:53/09:55 local, each

isolated in a managed worktree <project>/.claude/worktrees/<slug>,
both still alive (their processes survived the incident)

  • A file-history-snapshot line in session A's transcript at the /exit

moment references a trackingPath under ~/.claude/jobs/<other-session>/…,
i.e. cleanup-time bookkeeping was touching per-session job dirs under $HOME

Timeline (local time, from systemd journal + reconstructed transcripts)

| Time | Event |
|---|---|
| 16:40:15 | sysstat runs normally — home intact |
| 16:40:26 | Session C runs a normal (non-destructive) test script — succeeds |
| 16:40:35.770 | Session A receives /exit (transcript line recovered from disk); a file-history-snapshot entry is written in the same millisecond |
| 16:40:35 → 16:41:38 | Home directory contents recursively deleted (ext4 journal sequence shows directory entries being removed progressively in this window) |
| 16:41:38 | gnome-keyring cannot write ~/.local/share/keyrings/login.keyring (ENOENT); screen unlock shows keyring errors |
| 16:41:41 | Session A's terminal scope exits (systemd: ptyxis-spawn-*.scope consumed 1h52m CPU / 4h26m wall) |
| 16:41:50 | gpg-agent: "homedir has been removed - shutting down" |
| 16:41:50 | Session C receives a tool_result for a background task: output file ENOENT — "another Claude Code process in the same project deleted it during startup cleanup"; immediately followed by authentication_failed ("Not logged in") because ~/.claude/.credentials.json was gone |

Evidence that no shell command did it

All three session transcripts were reconstructed from the raw disk image
(deleted-block carving), and full memory dumps of the two surviving agent
processes were captured with gcore:

  1. Sessions B and C's recovered command histories contain **no recursive

delete of any path outside temp/mktemp directories**. The only rm -rf
occurrences in either process's 6 GB memory image are Claude Code's own
built-in safety documentation strings.

  1. Session A's transcript shows its last ~40 minutes were merge/verify git

operations; after /exit it wrote only its away-summary and terminated —
no tool call follows the /exit.

  1. The user ran no command (screen was locked during part of the window).
  2. No sudo/root involvement (system journal shows none; all deleted content

was uid-1000-owned; root-owned dirs in /home untouched).

  1. ext4 journal analysis: deletion performed with the user's uid, ordinary

unlink pattern (progressive directory-entry removal), i.e. a recursive
remove walking the tree — not a filesystem fault.

Why exit-time cleanup is the suspect

  • Exact temporal coincidence with /exit (sub-second alignment of the last

transcript entries and the start of the deletion window).

  • Two managed worktrees belonging to still-running background sessions

existed under <project>/.claude/worktrees/ at that moment — an unusual
state for cleanupWorktree/session teardown to handle (lock ownership held
by other live processes).

  • An in-process fs.rm(recursive) is the only mechanism consistent with

"no shell command anywhere + deletion as uid 1000 + starts at /exit".

  • The deletion root was $HOME itself: everything under /home/<user>

was removed (including ~/.claude, ~/.gnupg, ~/.ssh, ~/.config);
the home directory inode itself survived (still mounted as CWD by live
processes).

Impact

Complete loss of the user's home directory (~37 GB): dotfiles, SSH/GPG keys,
credentials, browser profile, project working tree including uncommitted
work, and all Claude Code state (~/.claude). Substantial recovery was only
possible via ext4 journal forensics and raw-image carving.

Suggested areas to inspect

  • /exit teardown path when the exiting session manages **other live

sessions'** worktrees/jobs (lock owned by another pid): any code path where
a worktree/session/job path variable can be empty or resolve to $HOME
before a recursive remove.

  • The file-history-snapshot written at /exit referencing another

session's ~/.claude/jobs/<id> path suggests cross-session cleanup was
active at that moment.

  • Absence of a final "guard" (refuse to recursively delete a path that is a

prefix of / equal to os.homedir(), or outside the managed
worktrees/jobs roots) in whatever remove ran here. A "screened delete
lane" concept exists in the binary (isScreenedDeleteLanePath,
isPathUnderManagedWorktreesDir) — this incident suggests some remove
bypassed those checks.

Reproduction (not attempted)

Not re-attempted for obvious reasons. The distinguishing state to reproduce:
interactive session in the main repo + two bg-kind sessions alive in
managed worktrees of the same repo + /exit in the interactive session.

Available on request (sanitized)

Reconstructed transcript excerpts (with user content redacted), systemd
journal excerpts, and the ext4-journal deletion-window analysis.

View original on GitHub ↗