Opus 4.8 extended thinking: Stop hook block re-entry corrupts thinking blocks → 400

Resolved 💬 2 comments Opened May 28, 2026 by jacobjove Closed Jul 2, 2026

Summary

When a Stop hook emits {"decision": "block", "reason": "..."} during an Opus 4.8 session with extended thinking enabled, Claude Code re-enters the conversation but corrupts the thinking/redacted_thinking blocks in the last assistant message before making the next API call. The API rejects with a 400.

Error

API Error: 400 messages.1.content.1: `thinking` or `redacted_thinking` blocks in
the latest assistant message cannot be modified. These blocks must remain as they
were in the original response.

Steps to reproduce

  1. Start a Claude Code session with Opus 4.8 (extended thinking enabled by default)
  2. Have any Stop hook registered that can emit {"decision": "block", "reason": "..."} — e.g. a verify gate, a recommend-before-asking gate, or any custom Stop hook
  3. Trigger the hook (e.g. end a turn with Claude claiming work is done without verification)
  4. Observe 400 on the re-entry API call

Expected behavior

CC preserves thinking/redacted_thinking blocks verbatim in the messages array when continuing after a Stop hook block decision, as required by the API contract.

Actual behavior

CC modifies the thinking block content (exact corruption mechanism unknown from hook-space; could be truncation, re-encoding, or type change) before passing the messages array to the API. The API rejects.

Workaround

Switch to a non-thinking model (e.g. Sonnet) for the session. Alternatively, suppress Stop hook block decisions when thinking blocks are present in the last assistant turn — but this silently disables all Stop hook safety checks for thinking-capable sessions, which is a significant behavioral loss.

Notes

  • dangerouslyDisableSandbox: true does not affect this — the corruption happens in CC's message-reconstruction logic, not the sandbox layer
  • The issue affects any Stop hook that emits a block decision; it is not specific to any particular gate or hook implementation
  • Workaround guard added locally in our hook setup (stop_gates.ts) to prevent the crash, but this suppresses all gate checks for thinking-block turns — not a real fix

View original on GitHub ↗

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