STUPID-2026-0105

Claude Code's session-cleanup routine deletes any user file whose name starts with digits, mistaking it for a dead process's stale PID file

4.0medium
March 14, 2026VerifiedReproducible
  1. Instruction given

    None — the deletion comes from Claude Code's own internal concurrentSessionCleanup() housekeeping routine, which runs automatically on session start, on autocompact, and periodically thereafter. No user command triggers it.

  2. Expected behavior

    The cleanup routine exists to remove stale `<pid>.json` lock files left behind by dead Claude Code processes in `~/.claude/sessions/`. It should only ever delete files that are actually named `<number>.json` for a PID that is confirmed not running — never a user's own files that merely happen to start with a digit.

  3. Actual behavior

    The routine lists every file in `~/.claude/sessions/`, strips a trailing `.json` if present, and calls `parseInt(name, 10)` on what's left with no check that the whole filename was numeric. JavaScript's `parseInt` reads only the leading digits it can parse and ignores the rest, so a file named like `2026-01-01_notes.md` parses as PID `2026`. If no process with that PID happens to be alive, and the platform isn't WSL (which is explicitly exempted), the file is deleted outright with `fs.unlink()` — no warning, no confirmation, and nothing logged. Because the routine reruns on every session start and autocompact, one session's cleanup pass can destroy a completely unrelated file another session left in the same directory moments earlier.

  4. Damage

    Any file a user places in `~/.claude/sessions/` with a digit-leading name — the reporter's own repro used a plain date-stamped notes file — is permanently unlinked rather than trashed, with no restore path. The bug reproduces on native Linux and macOS (WSL is the one platform explicitly skipped in the code), meaning the routine runs this way for the large majority of Claude Code's user base on every session start, autocompact, and periodic sweep — not as a one-off edge case. The reporter filed the issue with the exact vulnerable function, a minimal repro, and a one-line fix (validate the filename against `/^\d+\.json$/` before treating it as a PID file); labeled `bug`, `data-loss`, and `high-priority` by the maintainers, the issue was closed with zero comments and no visible maintainer response, fix, or explanation.

Claude Code ships a background routine, `concurrentSessionCleanup()`, that walks `~/.claude/sessions/` on every session start, autocompact, and periodic sweep to delete stale `<pid>.json` lock files left behind by dead processes. The function never checks that a filename is actually of the form `<number>.json` before treating it as one — it strips a trailing `.json` if present and hands whatever remains to `parseInt(name, 10)`, which in JavaScript reads only the leading digits and silently discards everything after them. A file named `2026-01-01_notes.md` parses as PID `2026`; if no process with that PID happens to be running, and the platform isn't WSL (the one explicitly exempted branch), the routine calls `fs.unlink()` on it directly — no confirmation, no warning, nothing logged, and no trash to recover it from. The reporter (`michaelk-q`) reproduced it in three steps: create a digit-prefixed file in the sessions directory, start a second session, watch the first file disappear. Because the cleanup reruns constantly and by design, this isn't a rare misfire — it's the routine's normal behavior on every native Linux and macOS install, silently destroying whatever a user happens to have dropped in that directory. The issue shipped with the exact faulty code path, a working repro, and a one-line fix (`if (!/^\d+\.json$/.test(file)) continue;`) already spelled out; Anthropic labeled it `bug`, `data-loss`, and `high-priority`, then closed it with zero comments and no maintainer response of any kind.

Classification

Failure mode
Destructive Action
Root cause
Logic Error
Domain
Other

Related incidents

Get told when an agent breaks something

We document AI agent failures daily, severity-scored against a published scale. When one lands at 7.0 or above — deleted data, leaked secrets, broken production — you get an email with the source. When nothing does, you get nothing.

This database is callable over MCP — query it from inside your agent.