[BUG] Print mode (-p --output-format stream-json): session ends with result: success while background tasks are running and a ScheduleWakeup is pending — scheduled wakeup can never fire, background tasks are killed
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?
Environment
- claude CLI 2.1.231
- Model: claude-fable-5,
--effort max - Linux guest VM (headless), spawned as:
claude --dangerously-skip-permissions --resume <sessionId> --model claude-fable-5 --effort max --output-format stream-json --verbose -p "<prompt>"
What happened
An agentic run doing long-horizon work started two background Bash tasks
(run_in_background: a ~10-minute build/test script and a codex exec
consultation), then called ScheduleWakeup (delaySeconds: 1500, prompt:<<autonomous-loop-dynamic>>) as a fallback heartbeat and ended its turn.
The ScheduleWakeup tool result actively confirmed the wait contract:
"Next wakeup scheduled for 12:01:00 (in 1538s). Nothing more to do this turn — the harness re-invokes you when the wakeup fires or a task-notification…"
Immediately after that turn ended, the CLI:
- emitted
{"type":"result","subtype":"success", ...}, - killed both background tasks (
task_updatedpatches with"status":"killed",
then task_notification messages with "status":"stopped"),
- exited 0.
The scheduled wakeup can never fire — the process is gone. The in-flight
background work (including a subprocess mid-consultation) is killed. The exit
code and result message both say success, so any orchestrator supervising
the process records a successful completion. Resuming the session, the model
itself describes the previous session as interrupted mid-work.
Reproduced twice back-to-back on the same session (the resumed attempt made the
same reasonable choice — background the long work, schedule a fallback wakeup,
yield — and died identically).
Final stream messages (sanitized, exact shapes)
{"type":"assistant", ... tool_use ScheduleWakeup {"delaySeconds":1500,"prompt":"<<autonomous-loop-dynamic>>","reason":"Fallback heartbeat while <background work> runs"}}
{"type":"user", ... tool_result "Next wakeup scheduled for 12:01:00 (in 1538s). Nothing more to do this turn — the harness re-invokes you when the wakeup fires or a task-notification..."}
{"type":"result","subtype":"success", ...}
{"type":"system","subtype":"background_tasks_changed","tasks":[{"task_id":"...","task_type":"local_bash","description":"..."}]}
{"type":"system","subtype":"task_updated","task_id":"...","patch":{"status":"killed","end_time":...}}
{"type":"system","subtype":"task_notification","task_id":"...","status":"stopped","output_file":"...","summary":"..."}
{"type":"system","subtype":"background_tasks_changed","tasks":[]}
{"type":"system","subtype":"task_updated","task_id":"...","patch":{"status":"killed","end_time":...}}
{"type":"system","subtype":"task_notification","task_id":"...","status":"stopped", ...}
(Note the post-result system messages: the kill/teardown notifications are
emitted AFTER the success result, so stream consumers that stop at result
never even see that background work was destroyed.)
What Should Happen?
Expected
One of:
- Print mode does not expose ScheduleWakeup (and its tool result does not
promise re-invocation), and a turn ending with running background tasks
either waits for them or ends with a result that reflects incompleteness; or
- Print mode honors the contract: sleep until the scheduled wakeup /
task-notifications and re-invoke the model, exiting only when the model ends
a turn with no pending wakeup and no running tasks.
Actual
The wait contract is offered and confirmed, then the process exitsresult: success / exit 0, killing the pending work.
Why it matters
Orchestrators (CI, agent farms, anything supervising claude -p) trust the
exit code and result message. This failure mode presents hours-long abandoned
runs as clean successes. The model is behaving reasonably — the tool result
explicitly told it yielding was correct.
Error Messages/Logs
Steps to Reproduce
claude --dangerously-skip-permissions --model claude-fable-5 --effort max --output-format stream-json --verbose -p "<prompt>"
where the prompt spawns long running tasks and it schedules a wakeup while it waits
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.231
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗