Hook callback ZodError (invalid_union, undefined control_response) in stdin reader is fatal to headless sessions

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 28, 2026

Summary

The CLI's control-protocol stdin reader throws an unhandled ZodError when a hook callback round-trip delivers undefined where the control_response union expects an object. The throw is fatal to the message-read loop: the subprocess dies before its first turn (headless runs record cost_usd: 0, session_id: null, and in some paths the misleading message Claude Code process aborted by user on fully headless runs where no user existed).

Error in hook callback hook_0
ZodError: [{ "code": "invalid_union", "errors": [[{"expected":"object","code":"invalid_type","path":[],"message":"Invalid input: expected object, received undefined"}], ...] }]
    at processLine (/$bunfs/root/src/entrypoints/cli.js:44718:1786)
    at read (/$bunfs/root/src/entrypoints/cli.js:44716:231)

Repro context

  • Headless (-p / stream-json) sessions spawned as subprocesses by an orchestration harness, with hooks registered (PostToolUse / SessionStart; also observed independently with sub-agent Stop hooks receiving undefined input).
  • Intermittent — the same workload dies or survives depending on whether the malformed round-trip lands during a critical read. One team observed 5 subprocess deaths in one evening across independent runs; the same fingerprint recurred across multiple weeks.

Expected

The adjacent code path already logs dropped control_response with malformed response payload for non-object payloads — the union-parse path at processLine should degrade the same way (drop + log) instead of throwing and severing the control stream. A hook-input/control_response payload of undefined should never be process-fatal.

Notes

Consumer-side we have guarded our own hook callbacks against undefined input and added a one-shot retry for zero-cost spawn deaths (Unity-Technologies/wtf-harness#1242), but the throw itself is inside the CLI's bundled reader and can't be fixed from outside.

View original on GitHub ↗