[Bug] Exit cleanup recursively deletes $HOME instead of session directory
[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 kindbg
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-snapshotline 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:
- 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.
- 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.
- The user ran no command (screen was locked during part of the window).
- No sudo/root involvement (system journal shows none; all deleted content
was uid-1000-owned; root-owned dirs in /home untouched).
- 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
$HOMEitself: 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
/exitteardown 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-snapshotwritten at/exitreferencing 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.