-p (headless) mode silently kills run_in_background Bash/agents at turn end — no notification loop exists

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

What happened

In claude -p (headless / print) mode, when the model launches a background task — either a Bash tool call with run_in_background: true, or an Agent/subagent (which defaults to background) — and then ends its turn, that task is orphaned and killed. The result event fires, the process exits, and any in-flight background work is terminated mid-execution. Its output is lost.

The model does this because the tool contract tells it background is safe: the Bash/Agent tool descriptions say background tasks run detached and "you'll be notified when one completes." But in single-shot -p mode there is no live process and no re-invocation loop to deliver that notification — the process exits at result. So the model makes a locally-correct decision based on the tool description, and the harness silently drops the work.

This is effectively a mismatch: -p exposes the same background tooling as interactive/streaming sessions, but without the notification/re-invoke loop that makes background tasks safe there.

Steps to reproduce

claude -p 'Run this in the BACKGROUND (run_in_background: true), then immediately end your turn without waiting: sleep 8 && echo done > /tmp/marker.txt' \
  --output-format stream-json --verbose --dangerously-skip-permissions
# observe: `result` fires within a couple seconds
ls /tmp/marker.txt   # never created — the backgrounded command was killed at turn end

The same happens with the Agent tool launched in its default (background) mode: the subagent is abandoned when the parent turn ends.

Expected behavior

At minimum, one of:

  1. Block turn-end while background tasks are still running in -p mode (wait for them, the way the interactive notification loop effectively does), or
  2. Disable/hide the background option for Bash and Agent tools when running under -p (force synchronous), or
  3. Emit a clear warning (a system event in the stream) when the model ends a turn with live background tasks, and/or tell the model in -p that no notification loop exists so background work will not complete.

Any of these would prevent silent data loss.

Actual behavior

Background tasks are silently killed at turn end. No error, no warning in the stream — from the outside it looks like the turn completed successfully while the actual work (a long import, build, test run, or subagent) never finished.

Why this is easy to miss

Most -p usage is single-shot synchronous work (CI, one-shot codegen, scripting) that never backgrounds anything, and interactive/SDK-streaming usage keeps the process alive so the notification loop works. The failure only surfaces in the narrow combination of headless single-shot -p + long-running background jobs + treating process-exit as "turn done." That combination is uncommon, which is likely why it hasn't been widely reported — but the silent, successful-looking data loss makes it a sharp edge when you do hit it.

Environment

  • Claude Code CLI 2.1.216
  • macOS (Darwin), Apple Silicon
  • Invocation: claude -p <prompt> --output-format stream-json --verbose --dangerously-skip-permissions [--resume <id>]

Workaround (for others hitting this)

Append a system prompt (--append-system-prompt) instructing the model to run everything in the foreground and never end a turn while any command/subagent is still running — a slow foreground command is fine, the session just stays alive. This is only advisory (relies on model compliance); a harness-level guarantee (option 1 or 2 above) would be the real fix.

View original on GitHub ↗