anthropics / anthropics/claude-code
[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)
- Langage dominant
- Python
- Étoiles
- 145k
- Forks
- 23.1k
- Métriques de merge des PR
- Métriques de PR en attente
Description
## 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
```shell
# 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):**
```sh
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):**
```sh
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.
Guide de contribution
Aucun guide de contribution indexé pour ce dépôt
Piste de recherche
Start by running the two provided `claude -p` reproduction commands with `CLAUDE_CODE_AUTO_BACKGROUND_TIMEOUT_MS=10000`, comparing the control and `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` treatment. Trace the print-mode background-task lifecycle and the background-message selection, including `CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS`. Done means a backgrounded command in print mode either completes and reports its output or exits with a visible failure, never a false success with lost work.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- macos, python, shell, zsh
- Domaine
- cli, devtools, testing
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- Active
- Clarté
- Plutôt claire
- Accessibilité débutants
- 45/100