[BUG] -p mode: Bash auto-background tells the model "You will be notified when it completes" — a promise print mode never keeps (silent data loss)

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

Preflight Checklist

  • [x] I have searched existing issues. Closest matches are adjacent, not this bug: #73453 (headless exit with pending Agent-tool children; ceiling not applied), #80834 (same false copy, but scoped to subagents' Bash tasks), #82151 (turn-end vs live-work signal, interactive session), #81476 (Monitor tool copy in headless), #59864 (closed precedent: tools advertised in -p without execution semantics). None covers a top-level claude -p session with an auto-backgrounded Bash command.
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (reproduced on 2.1.245 today)

What's Wrong?

In non-interactive print mode (claude -p), a Bash tool call that exceeds the auto-background threshold is moved to the background and the model is told, verbatim: "You will be notified when it completes."

In interactive sessions that promise is kept — the harness re-invokes the model on completion. In print mode the session terminates the moment the model ends its turn: a model that trusts the harness and ends its turn to wait dies mid-task, the backgrounded command is killed with the process, and the CLI exits with is_error: false whose result is a mid-work status note. The work is silently lost while the envelope reads success.

The kicker: the truthful message already ships in the binary. The 2.1.245 background-message builder contains a second variant:

"If it exits while you are still working you will be notified, but it is terminated when you give your final response and no notification can follow that — so do not end your turn to wait for it; if you need its result, wait for it before giving your final response."

…but it is selected only for commands owned by a synchronous subagent (the backgroundEndsWithFinalResponse / reaped-at-final-response path, whose schema doc reads: "…terminated when that agent gives its final response; absent when the command survives (main loop, async subagents)"). In print mode the main-loop command does not survive — the classification is wrong there, so the false promise is emitted.

What Should Happen?

Any one of these (smallest first):

  1. Truthful message in print mode — select the already-shipping "terminated when you give your final response / do not end your turn to wait" variant when the session is non-interactive. One condition change; removes a whole class of silent mid-task deaths.
  2. Default CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 in -p mode (overridable): long commands then block in-turn or fail visibly at the tool timeout. Verified working as a per-run mitigation (repro below).
  3. Actually keep the promise: hold the event loop open while background tasks are pending and deliver the notification. The machinery appears to exist — the binary carries CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS ("…terminating. Set CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0 to wait indefinitely.") — but it did not hold the process for an auto-backgrounded Bash task (see timing below; cf. #73453 for the Agent-tool analogue).

Error Messages/Logs

# Control run on 2.1.245 (full pipeline wall time: 18.4s — the process exited
# ~8s after the 10s auto-background trigger; the 40s command never finished):
{
  "is_error": false,
  "subtype": "success",
  "num_turns": 3,
  "result": "The command is running in the background. It will complete in approximately 40 seconds. I'll report the output once it finishes."
}
# "ctl-marker-2245" is never printed anywhere. Exit code 0.

Steps to Reproduce

Deterministic, ~18 seconds end to end. CLAUDE_CODE_AUTO_BACKGROUND_TIMEOUT_MS is only used to trigger the auto-background quickly; the default threshold reproduces identically, just slower.

1. Bug (control):

CLAUDE_CODE_AUTO_BACKGROUND_TIMEOUT_MS=10000 \
claude -p 'Run exactly this shell command and then report its full output verbatim: sleep 40 && echo ctl-marker-2245' \
  --model haiku --output-format json --permission-mode bypassPermissions

Observed: at ~10s the Bash call is auto-backgrounded and the tool result tells the model "You will be notified when it completes." The model ends its turn to wait; the CLI prints the envelope above and exits at ~18s. The marker is never delivered.

2. Correct behavior exists behind an env var (treatment):

CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 CLAUDE_CODE_AUTO_BACKGROUND_TIMEOUT_MS=10000 \
claude -p 'Run exactly this shell command and then report its full output verbatim: sleep 25 && echo fix-marker-2245' \
  --model haiku --output-format json --permission-mode bypassPermissions

Observed: the Bash call blocks in-turn and result contains fix-marker-2245 (is_error: false, 33s wall). So the capability to avoid the loss already ships; the print-mode default is the foot-gun.

Real-world impact

We found this when a claude -p review subagent (long-effort model) ran a slow test suite; the suite was auto-backgrounded; the harness told the model it would be notified; the model ended its turn "waiting for its notification" (verbatim from its transcript) and the process exited. 19 minutes of analysis were lost; the parent pipeline received exit 0 with a status note as the final answer. Any headless pipeline that trusts is_error: false receives a truncated deliverable with no failure signal — a silent-data-loss class, not cosmetic. Comment 3 on #73453 (terminal_reason: "completed", result = "Good progress — 7 of 20 agents have completed so far. Waiting…") shows the same failure shape via the Agent tool.

Claude Model

Other — haiku for the minimal repro; original incident hit a long-effort seat. Model-independent: any model that believes the tool result reproduces it.

Is this a regression?

I don't know

Claude Code Version

2.1.245 (Claude Code) — repro verified 2026-08-25 on the darwin-arm64 npm dist binary; also reproduced same day on 2.1.233 (native installer).

Platform

Anthropic API

Operating System

macOS (Darwin 25.6.0, arm64)

Terminal/Shell

Non-interactive/CI environment (claude -p, zsh parent shell)

Additional Information

  • Env knobs confirmed in the shipped binary and by behavior: CLAUDE_CODE_AUTO_BACKGROUND_TIMEOUT_MS (trigger), CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 (working mitigation), CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS (present in binary; did not apply to this Bash case).
  • Related: #73453, #80834, #82151, #81476; #59864 is closed precedent for aligning print-mode tool copy with print-mode semantics.
  • Happy to provide the full --output-format stream-json traces for both runs if useful.

View original on GitHub ↗