Vertex/Bedrock: `thinking.display:"summarized"` stripped from request body → empty thinking on Opus 4.8/4.7 (regression 2.1.150→2.1.177, still in 2.1.193)
Summary
On the Vertex (and Bedrock) provider path, Claude Code strips thinking.display from the request body, so the server applies the Opus 4.8 / 4.7 default display:"omitted" and returns thinking blocks with a signature but empty thinking text — even when the user explicitly passes --thinking adaptive --thinking-display summarized.
This is a regression: it works on 2.1.150, and is broken from 2.1.177 through 2.1.193 (latest). The direct Anthropic API path is unaffected — the same CLI version returns non-empty summarized thinking on first-party. The discriminator is purely the provider.
This is likely the underlying root cause behind #56356 ("even passing --thinking adaptive --thinking-display summarized does not fix it") for Vertex/Bedrock users, and is distinct from the first-party "harness doesn't request display" angle in #63358 / #49268.
Environment
- CLI:
@anthropic-ai/claude-code2.1.193 (GIT_SHA a1938d2a, build 2026-06-25), native binary; also reproduced on 2.1.177 (GIT_SHA 6fae7a0). - Last working version: 2.1.150 (
GIT_SHA 28d4819). - Provider: Vertex (
CLAUDE_CODE_USE_VERTEX=1,CLOUD_ML_REGION=global). - Model:
claude-opus-4-8[1m]. Thinking:--thinking adaptive --thinking-display summarized,--effort high.
Minimal repro
unset ANTHROPIC_API_KEY
export CLAUDE_CODE_USE_VERTEX=1 CLOUD_ML_REGION=global ANTHROPIC_VERTEX_PROJECT_ID=<your-project>
export MAX_THINKING_TOKENS=6000 # only to make adaptive reliably emit a thinking block for the test
claude -p --output-format stream-json --verbose --include-partial-messages \
--model "claude-opus-4-8[1m]" --thinking-display summarized \
"Reason step by step, then answer: a bat and ball cost \$1.10; the bat is \$1.00 more than the ball. How much is the ball?"
Expected: a thinking content block with non-empty summarized text (plus signature).
Actual: a thinking block with signature present but thinking == "" and zero thinking_delta events.
Evidence
1. The display field is dropped from the Vertex request body
Captured the exact outgoing request (via CLAUDE_CODE_SKIP_VERTEX_AUTH=1 + a local ANTHROPIC_VERTEX_BASE_URL), same flags in all cases:
| Binary / provider | thinking on the wire | Result |
|---|---|---|
| 2.1.177 Vertex | {"type":"adaptive"} ← display missing | empty thinking |
| 2.1.193 Vertex | {"type":"adaptive"} ← display missing | empty thinking |
| 2.1.150 Vertex | {"type":"enabled","budget_tokens":31999,"display":"summarized"} | works |
| 2.1.177 Anthropic (first-party) | {"type":"adaptive","display":"summarized"} | works |
2. Live Vertex confirmation (2.1.193, Opus 4.8)
| 2.1.193 Vertex | thinking block | signature | thinking text | thinking_delta |
|---|---|---|---|---|
| default | present | 396 chars | 0 (empty) | 0 |
| + CLAUDE_CODE_EXTRA_BODY='{"thinking":{"type":"adaptive","display":"summarized"}}' | present | 540 chars | 224 chars ✅ | 22 |
Root cause
The per-request body builder gates the thinking.display value behind a first-party-only provider allow-list that omits vertex/bedrock.
2.1.150 (working) — no provider gate:
oH = (thinkingEnabled) ? q.display : void 0 // display always forwarded
// -> {type:"adaptive", display:oH} / {type:"enabled", budget_tokens, display:oH}
2.1.177 / 2.1.193 (broken) — new gate:
// 2.1.193 symbol names shown:
Vr = Un && YO() && FOt(u) ? n.display : void 0 // Un = thinking enabled
function YO(){ return w4r() && !b$e() }
function w4r(){ let e = yr(); return e==="firstParty" || e==="anthropicAws" || e==="foundry" } // omits "vertex"/"bedrock"
function b$e(){ return at(process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS) || C8("hipaa") }
function yr(){ ... process.env.CLAUDE_CODE_USE_VERTEX ? "vertex" : "firstParty" }
// -> {type:"adaptive", display: Vr} with Vr === undefined on Vertex -> serializes to {"type":"adaptive"}
With CLAUDE_CODE_USE_VERTEX, yr()==="vertex", which is not in w4r()'s allow-list ⇒ YO() is false ⇒ Vr becomes undefined ⇒ display is omitted from the body ⇒ server defaults to display:"omitted". (In 2.1.177 the identical logic is S9 = r9 && NN() && tW_(A) ? q.display : void 0, Ek8(){return H==="firstParty"||H==="anthropicAws"||H==="foundry"}.)
thinking.display is a provider-agnostic field (it worked on Vertex in 2.1.150 and the underlying API supports it), so folding it behind the first-party "experimental betas" provider gate is incorrect.
Scope / blast radius
yr()can returnbedrock, vertex, foundry, firstParty, anthropicAws, mantle, gateway. The allow-listw4r()only includesfirstParty, anthropicAws, foundry⇒vertex,bedrock,mantle,gatewayall losethinking.display.- Because of
b$e(), first-party also breaks ifCLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1or HIPAA mode is enabled.
Suggested fix
Either (a) add vertex/bedrock (and mantle/gateway) to the w4r()/Ek8() allow-list, or — cleaner — (b) stop gating thinking.display passthrough on YO()/NN() entirely and forward q.display whenever thinking is enabled, restoring the 2.1.150 behavior. thinking.display is not an experimental-beta concern.
Acceptance criteria
- Vertex request body contains
thinking.display:"summarized"when--thinking-display summarizedis set; non-empty summarized thinking is returned forclaude-opus-4-8[1m]. --thinking-display omittedstill returns signature-only / empty thinking.- Works with and without
--include-partial-messages; existing thinking-block signatures remain present.
Workaround (until fixed)
export CLAUDE_CODE_EXTRA_BODY='{"thinking":{"type":"adaptive","display":"summarized"}}'
CLAUDE_CODE_EXTRA_BODY is spread into the body after the CLI's thinking object, re-injecting display on the wire (verified live, see table above). Must use type:"adaptive" — Opus 4.8 rejects type:"enabled" with 400 "thinking.type.enabled" is not supported for this model.
Related
- #63358 (Opus 4.8 empty thinking — first-party), #49268, #56356 (this is the Vertex/Bedrock root cause for "the flag doesn't fix it").
- SDK side (downstream impact for
claude-agent-sdkusers on Vertex): anthropics/claude-agent-sdk-python — filed separately and linked below.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗