Headless stream-json crashes with 'Cannot read properties of undefined (reading input_tokens)' on default model (Opus 4.7)

Resolved 💬 3 comments Opened Apr 17, 2026 by cokoro Closed Apr 20, 2026

[Bug] Headless mode crashes with "Cannot read properties of undefined (reading 'input_tokens')" on default model (Opus 4.7)

Summary

Running claude -p --output-format stream-json --input-format stream-json in headless mode crashes immediately with:

Cannot read properties of undefined (reading 'input_tokens')

The crash reproduces on a fresh workdir with no --resume and no prior session file — so it is not a corrupted-session issue. The same exact command line with --model claude-opus-4-6 added completes successfully, which strongly suggests the default model (Opus 4.7 in my case) produces a response shape that the usage parser doesn't handle.

Environment

  • Claude Code: 2.1.112 (latest stable, installed via winget)
  • OS: Windows 11 Enterprise 10.0.26200
  • Shell: Git Bash (MSYS2)
  • Default model: Opus 4.7 (1M context)
  • Wrapper: Multica local agent daemon spawning claude as subprocess. Same issue occurs with manually-invoked claude -p in the same mode.

Reproduction

Headless stream-json invocation without --model:

claude -p --output-format stream-json --input-format stream-json \
  --verbose --strict-mcp-config --permission-mode bypassPermissions

Fails within ~7 seconds with the input_tokens error. The synthetic error message gets persisted to ~/.claude/projects/<proj>/<sid>.jsonl as an assistant message with model "<synthetic>" and isApiErrorMessage: true, meaning subsequent --resume attempts read the corrupted history and either fail the same way or propagate the error.

Comparison — failing vs working commands

All four commands below ran against the same daemon, same workdir root, within a 35-minute window. The only meaningful difference is the presence of --model claude-opus-4-6.

❌ Fails (uses default model = Opus 4.7)

claude -p --output-format stream-json --input-format stream-json \
  --verbose --strict-mcp-config --permission-mode bypassPermissions
claude -p --output-format stream-json --input-format stream-json \
  --verbose --strict-mcp-config --permission-mode bypassPermissions \
  --resume <sid>

Both fail at ~3–10s with Cannot read properties of undefined (reading 'input_tokens').

✅ Works (explicit --model claude-opus-4-6)

claude -p --output-format stream-json --input-format stream-json \
  --verbose --strict-mcp-config --permission-mode bypassPermissions \
  --model claude-opus-4-6
claude -p --output-format stream-json --input-format stream-json \
  --verbose --strict-mcp-config --permission-mode bypassPermissions \
  --resume <sid> --model claude-opus-4-6

Runs to completion, tool calls work, session resumes correctly.

Corrupted session artefact

After a crash, the session JSONL contains an entry like this (verbatim, trimmed):

{
  "type": "assistant",
  "message": {
    "id": "bdccefb0-0407-4e11-bd59-42674cd0a59b",
    "model": "<synthetic>",
    "role": "assistant",
    "stop_reason": "stop_sequence",
    "usage": {
      "input_tokens": 0,
      "output_tokens": 0,
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 0
    },
    "content": [
      { "type": "text", "text": "Cannot read properties of undefined (reading 'input_tokens')" }
    ]
  },
  "isApiErrorMessage": true,
  "version": "2.1.112"
}

This makes the failure sticky: once one task crashes, every subsequent --resume of that session appears to re-trigger the same path (8–10s, 0 tools).

Likely root cause

The usage accumulator in stream-json output likely reads response.usage.input_tokens without a null check. With Opus 4.7 default model, some early streamed message (possibly an empty/reasoning-only chunk, tool-use delta, or cache init response) has usage === undefined, triggering the TypeError.

Two independent issues seem to be in play:

  1. Defensive-coding miss: usage should be guarded — a missing field shouldn't abort the run.
  2. Model-specific trigger: Opus 4.7 + headless stream-json hits this path consistently; Opus 4.6 doesn't.

Impact

  • All headless agent wrappers (Multica, OpenHands-style, custom CI bots) that don't hard-code --model cannot use the CLI's default model.
  • The error is persisted to session history, poisoning --resume and confusing users into thinking their session file is corrupted (I spent ~1 hour debugging session files before realizing the issue was model selection).

Workaround

Add --model claude-opus-4-6 (or any Sonnet variant) explicitly to every headless invocation. This should be documented until the usage-parse bug is fixed.

Suggested fix

  1. Guard response?.usage?.input_tokens ?? 0 at the usage-accumulation site.
  2. Don't persist <synthetic> error messages to the session JSONL, or mark them so --resume skips them.
  3. Add a regression test for --output-format stream-json with each supported model.

Logs

Daemon log excerpts (timestamps from 2026-04-17):

15:58:55 starting agent workdir=...\53360a05\workdir model="" reused=false
15:58:55 agent command exec=claude args="[-p --output-format stream-json ... --permission-mode bypassPermissions]"
15:59:02 agent text="Cannot read properties of undefined (reading 'input_tokens')"
15:59:04 claude finished status=failed duration=9.764s

16:28:29 starting agent workdir=...\0523067c\workdir model="" reused=false
16:28:29 agent command exec=claude args="[-p ... --permission-mode bypassPermissions --model claude-opus-4-6]"
16:28:39 tool #1: Bash
... (runs to completion)

Happy to provide full stack traces or the poisoned JSONL if that helps.

View original on GitHub ↗

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