Malformed tool-call emission silently swallows execution + notification (post-background-completion wake-stall)

Resolved 💬 2 comments Opened Jun 17, 2026 by Benjamin-Sterrett Closed Jun 21, 2026

Summary

A malformed function-call emission — where the model's tool call serializes as literal assistant text with a corrupted wrapper token instead of a real antml function-call block — is silently dropped by the harness: nothing executes, no result block is returned, and no completion/error notification fires. When this happens on the continuation turn that wakes a session after a background task completes, the session goes idle again with no signal. From the operator's view, "Claude woke and then never did anything" — a silent wait-deadlock with no error, no timeout, no second ring.

Impact

  • Severity: high for autonomous/background workflows. Long autonomous runs dispatch work as background tasks (run_in_background:true) and rely on the completion notification to resume. If the resume turn's first tool call garbles, the run silently stalls and a human must notice and poke it.
  • No layer can self-recover: PreToolUse/PostToolUse hooks never fire (a malformed emission is not a tool invocation); the model receives no "empty result" event to react to (from its view it made the call and the turn ended); external task-watchdogs have no task ID to monitor (the background task already completed — the stall is in the consumer turn).

Reproduction (observed instance)

  1. Session dispatches a Bash command with run_in_background:true, ends its turn, awaits the <task-notification>.
  2. The background task completes; the notification fires; the wake/continuation turn begins.
  3. The wake turn's first tool call (a cat <output-file> read) renders as assistant TEXT with a stray wrapper token where the function-call block opening should be:

``
court
<invoke name="Bash">
<parameter name="command">cat /private/tmp/.../tasks/<id>.output 2>/dev/null | tail -6
...</parameter>
</invoke>
``

(Note the literal court token instead of the correct antml opening; the inner invoke/parameter shape is otherwise intact and the command content is valid.)

  1. The harness does not parse this as a tool call → executes nothing → returns no result → the turn ends → the session idles. Re-issuing the identical command on the next turn runs fine, confirming the content was valid and only the emission was malformed.

Expected behavior (any one would resolve the deadlock)

  1. Detect-and-error: when assistant output contains a function-call-shaped block that fails to parse as a real tool call, surface a visible error to the session (an "empty result" / parse-failure event the model can react to and re-issue), rather than rendering it as inert text.
  2. Notify on no-op wake: if a turn that began from a <task-notification> ends without executing any tool call and without producing a user-directed message, emit a diagnostic/notification rather than silently idling.
  3. Tolerant parsing: recognize and recover the near-miss wrapper token (e.g. the stray court) so the intended call executes.

Environment

  • Claude Code (CLI), macOS (darwin). Reproduced across multiple repos/sessions (dev-workflow, backlit) on 2026-06-17.
  • Also observed in the Kimi runtime variant, suggesting the malformed-emission class is not specific to one model backend.

Local mitigations in place (not a substitute for the harness fix)

  • Lead-side discipline: a background dispatch is "in flight" only once a real task ID appears in the result; an empty return means the call did not fire → re-issue immediately (tracked internally as PRJ-1817).
  • A transcript-tailing detector that flags a function-call-shaped emission rendered as assistant text on a post-wake turn and alerts the operator (tracked internally as PRJ-1818). This is a backstop; the silent-swallow root cause is in the harness.

View original on GitHub ↗

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