[BUG] Task-output read errors always claim "another Claude Code process ... deleted it during startup cleanup" — fixed string appended to every read failure, misdirecting debugging

Status Open
Reported on v2.1.241
Maintainer reply None cached
Activity 1 comment · opened Aug 24, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

When a Bash task's output file cannot be read, Claude Code appends a fixed sentence to the error:

This usually means another Claude Code process in the same project deleted it during startup cleanup.

The sentence is appended unconditionally, on every read failure, regardless of the error code — ENOENT, EACCES, EIO, anything — and without checking any evidence that such a process existed or acted. It is a hard-coded guess rendered as a diagnosis.

The read-side handler in the shipped bundle (2.1.241, decompiled excerpt; identifiers are minified):

catch(t){ let r = t instanceof Error && "code" in t ? String(t.code) : "unknown";
  return `<bash output unavailable: output file ${this.path} could not be read (${r}). ` +
         "This usually means another Claude Code process in the same project deleted it during startup cleanup.>" }

Two properties:

  1. Unconditional on the error. ${r} interpolates whatever code the failure carried; the "startup cleanup" sentence is identical in every case. A permission failure gets "explained" as a peer deletion.
  2. No evidence is consulted. Nothing checks whether another Claude Code process ran, whether a startup occurred in the window, or whether the configured retention (cleanupPeriodDays) even permits deletion. On the machine where this was measured, retention was set to 9999 days — making the named mechanism impossible — and the message asserted it anyway, verbatim, 24 times.

Why this matters

The message converts a generic I/O failure into a confident, specific, usually-unverifiable accusation, and it misdirects debugging at scale:

  • In a measured incident (24 emissions over 6 days), the real deleter was a local test script sweeping /tmp — but "the CLI already told us what happened" sent the investigation toward Claude Code's startup path for days, including a full bundle decode hunting for a peer-cleanup deleter that (with retention disabled) does not exist.
  • The same happens for every user who hits an unreadable task-output file for any other reason: tmpwatch/tmpreaper, containers with ephemeral /tmp, antivirus, disk faults. Issue threads show users repeating the message's diagnosis as established fact.
  • The wording appears to be a fossil: #42536 (April 2026) describes an era when sessions genuinely did delete each other's output files during startup cleanup, before session-id namespacing of the task dirs. The collision was fixed; the accusatory message survived.

An error message is load-bearing diagnostic surface. A wrong-but-confident one is worse than none.

What Should Happen?

State the fact and the error code; drop or genuinely hedge the causal assertion. Smallest change:

<bash output unavailable: output file <path> could not be read (ENOENT).>

optionally followed by a hedged hint gated on the error code:

Possible causes: the file was removed by an external tmp cleaner, another process, or (if retention cleanup is enabled) Claude Code's own startup cleanup.

Verifying the cause before asserting it (check for a recent peer startup; honor cleanupPeriodDays) would be strictly better but is more code than a message needs.

Steps to Reproduce

Executed 2026-08-24 against 2.1.241 (Linux/WSL2):

  1. In a running session, launch a background Bash task: sleep 90 && echo done.
  2. Delete its output file from the same session (or any external means) while the task runs:

rm -f $TMPDIR/claude-<uid>/<project-slug>/<session-id>/tasks/<task-id>.output

  1. Request the task's output while the task is still running.

Expected: an error stating the file could not be read (ENOENT).

Actual (observed verbatim; project/session path segments elided):

<bash output unavailable: output file /tmp/.../claude-1000/<project-slug>/<session-id>/tasks/ba31blud3.output could not be read (ENOENT). This usually means another Claude Code process in the same project deleted it during startup cleanup.>

No other Claude Code process touched the file — the deleter was the same session's own rm, one tool call earlier — and the message asserted the peer-startup cause anyway.

Timing note for anyone reproducing: the harness's exit-time append recreates the output file, so deleting it after completion self-heals. The message fires when the file is read while missing — a mid-run read, or the containing directory removed (the common external-cleaner shape).

Prior art (context, not duplicates)

  • #42536 → closed as duplicate of #32252 (itself closed NOT_PLANNED/stale, never triaged) — describes the pre-namespacing era when the message's claim was true.
  • #75354 — the retention/cleanup design discussion; adjacent, but about cleanup behavior, not the message.
  • #86222 — a different defect (persistent EACCES on the task dir) whose reporters also received misleading messaging.

None of these asks for the message itself to be fixed; this report is only about the message.

Environment

  • Claude Code 2.1.241
  • Linux (WSL2), cleanupPeriodDays: 9999

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗