Headless -p run exits 0 mid-task: empty assistant turn after successful tool call treated as normal end-of-turn

Resolved 💬 2 comments Opened Jun 12, 2026 by nobles92ts-ship-it Closed Jun 16, 2026

Summary

In long non-interactive runs (claude -p via SDK entrypoint), the session occasionally ends silently in the middle of a task: right after a successful tool call, the next assistant turn's API stream starts (first chunk received) but produces zero content blocks, and the CLI treats this as a normal end-of-turn — Stop hooks run, MCP servers tear down cleanly, and the process exits 0 with the task half-finished. No error, no retry, nothing on stderr.

Environment

  • Claude Code v2.1.175 (cc_version=2.1.175.96a; cc_entrypoint=claude-desktop, Agent SDK / headless -p)
  • Windows 11 Pro 10.0.26200
  • Model: claude-sonnet-4-6, permission_mode: bypassPermissions, --debug-file enabled
  • Workload: orchestrated pipeline step, ~25–40 min per invocation, hundreds of tool calls (Read/Write/Edit/Bash + MCP)

Evidence (from --debug-file, two occurrences in one run, ~10 min apart)

Occurrence 1 — the model had just written part 1 of a multi-part output file (clearly mid-task), the Write tool succeeded, then:

10:04:38.769 [INFO ] [Stall] tool_dispatch_end tool=Write toolUseId=toolu_01EafsV1kPPto4DXWzLfWjMc outcome=ok durationMs=16
10:04:39.124 [DEBUG] [API REQUEST] /v1/messages x-client-request-id=1bd1c525-0248-49ab-83db-ba2b2db2b55b source=sdk
10:04:41.063 [DEBUG] Stream started - received first chunk
10:04:41.064 [DEBUG] [API:timing] first byte after 1943ms
   (no content_block / tool_use / text events logged after this point)
10:04:42.440 [DEBUG] Hooks: Registering async hook async_hook_33580 (Stop) with timeout 600000ms
10:04:42.612 [DEBUG] Hook Stop (Stop) success:
10:04:42.630 [DEBUG] MCP server "...": Terminating MCP server process tree   (all servers close "cleanly")

Occurrence 2 — same signature on the re-run of the same step:

10:14:21.910 [DEBUG] [API REQUEST] /v1/messages x-client-request-id=2849ceb7-973e-4987-8b67-ba2941580bab source=sdk
10:14:23.807 [DEBUG] Stream started - received first chunk
10:14:23.808 [DEBUG] [API:timing] first byte after 1902ms
10:14:25.578 [DEBUG] Hooks: Registering async hook async_hook_30288 (Stop) with timeout 600000ms
10:14:25.769 [DEBUG] Hook Stop (Stop) success:

In both cases:

  • The immediately preceding tool call succeeded (outcome=ok), and the assistant's prior text explicitly announced it would continue (write part 2, run validation, etc.).
  • Between "first chunk" and Stop-hook registration there are no content events and no error/retry lines — only an unrelated source=side_query request.
  • stderr is empty, exit code is 0, shutdown is indistinguishable from a successful completion.

The third invocation of the identical prompt/step completed normally (~27 min, full output), so this is intermittent, not prompt-related.

Expected

An assistant turn that streams zero content blocks mid-conversation (especially right after a tool_result) should be treated as an anomaly: retried by the client, or surfaced as an error / non-zero exit — not silently accepted as a normal end-of-turn.

Actual

The empty turn is accepted as end-of-turn; the run exits 0 mid-task. For unattended/orchestrated use this is the worst failure mode: downstream automation sees a "successful" exit with incomplete artifacts.

Frequency / impact

Across ~10 long pipeline runs over several weeks we hit this repeatedly (it is our #1 cause of unattended-run failures). In the run quoted above it happened on 2 of 3 attempts of the same step within 10 minutes.

Workaround we use

An external watchdog validates step artifacts after each -p invocation and re-invokes the CLI with a continuation prompt when the output is incomplete. It works (auto-recovery in ~2 s), but the silent rc=0 makes detection purely heuristic — a non-zero exit or an empty_turn error event would make this robust.

Happy to provide the full debug log (2.3 MB) privately if useful — the x-client-request-id values above should let you locate the server-side responses.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗