Session spontaneously re-executed hours-old background Bash commands (raw '&') — original order, ~2.3min apart, truncating their logs
Summary
Four historical background Bash commands (long-running processes started with a trailing & inside Bash tool calls) were spontaneously re-executed hours later by an interactive Claude Code session, in their original order, ~2.3 minutes apart, with no user or model action requesting them. Each re-execution truncated the log file the original command had been writing (> logfile in the replayed command line), destroying evidence and initially masquerading as worker crashes.
We could not reproduce this on demand — this report is the forensic evidence from one occurrence, which we believe is enough to point at the responsible code path.
Environment
- Claude Code 2.1.239, Linux (Ubuntu, kernel 7.0.0-29-generic), node v22.22.2
- Interactive session inside tmux, running continuously for ~35 hours at the time of the event
- The session had earlier launched four long-running
codex exec … > logfile 2>&1 &style background commands via the Bash tool (raw&, not the Bash tool'srun_in_backgroundmode)
What happened
- Aug 22, ~19:0x–20:0x local: four new bash process trees appeared, each re-running one of the session's four historical raw-
&background command lines, in original launch order, spaced ~2.3 min apart. - Each replayed tree was launched with the session's own shell-snapshot preamble:
bash -c 'source ~/.claude/shell-snapshots/snapshot-bash-<id>.sh; eval <historical command>'— i.e., the exact idiom Claude Code itself uses to spawn Bash tool commands, referencing this session's specific snapshot file. - The replays truncated the original commands' log files (the
>redirections re-executed) and raced a git worktree the originals had used. - We noticed and killed the four replayed trees by PID; no further replays occurred.
Evidence that the origin is the session's own harness
- cgroup containment: all four replayed trees ran inside the same transient systemd scope (cgroup) as the session's tmux pane — the scope that contains the interactive
claudeprocess. An external tool would have run in its own cgroup. The trees were re-parented to the user systemd manager, i.e. their direct spawner was a process inside that pane's tree which exited after spawning. - Launch idiom: the replays sourced this session's own shell snapshot file by exact path. That path is not present in the session transcript; only the harness composes launches this way.
- No process restart: the main
claudeprocess had been running uninterrupted since the previous morning (verified viaps lstart), so this was not a session restart re-running anything at startup — whatever replayed did so from inside the still-running process, which is why we suspect an internal background-shell-manager restart/recovery replaying a journal or task list of previously issued background commands. - Ruled out: no cron/systemd timers matching the cadence were active (independently verified); no local tooling on this machine references shell snapshots (repo-wide grep); other agent sessions on the machine run in different panes/cgroups.
Impact
The replayed commands happened to be model-invocation workers, but the same mechanism re-executed arbitrary historical Bash command lines verbatim. If a session's journal of background commands can replay after hours, command lines with side effects (git push, deployments, deletions) are equally exposed. The log-truncation side effect also destroys the evidence of the original runs, which made this hard to diagnose (it initially presented as three separate worker crashes).
Workaround
Long-running background work started via the Bash tool's run_in_background mode was not observed to replay. We have moved all long-running work off raw & onto run_in_background as a guard.
What we could not determine
Anything about the internal trigger — whether an in-process shell-manager restarted and replayed state, what that state is, or what prompted it at that moment (~35h into the session). Happy to provide additional forensics (ps captures, exact timestamps, scope names) on request.
🤖 Generated with Claude Code
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗