You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix: thinking block signature lost when model differs, breaking multi-turn with extended thinking #22813
When using an Anthropic model with extended thinking enabled, the following error appears mid-conversation:
The model returned the following errors: messages.N.content.1: thinking or redacted_thinking blocks in the latest assistant message cannot be modified.
Root cause is in packages/opencode/src/session/message-v2.ts. When reconstructing conversation history, reasoning parts strip providerMetadata when the message was produced by a different model:
This is correct for text and tool parts — it prevents provider-specific data (e.g. OpenAI itemId) from leaking into other providers. But Anthropic's thinking blocks carry a cryptographic signature inside providerMetadata. Anthropic verifies this signature on every subsequent turn. If it is missing, the block is considered modified and the request is rejected.
The fix is to always preserve providerMetadata for reasoning parts regardless of the differentModel flag.
Steps to reproduce
Enable extended thinking on a Claude 3.7+ model
Have a conversation long enough to accumulate thinking blocks in history, or switch models mid-session and switch back to Anthropic
Continue the conversation — the error fires on any turn that replays a thinking block without its signature
Confirming this reproduces on the Electron desktop app v1.15.13 (macOS, arm64) — not just the CLI. Your root-cause analysis (message-v2.ts stripping providerMetadata when differentModel === true) matches the symptom exactly.
Trigger in my setup:
opencode.json declares thinking for claude-opus-4-6-20250514.
The active session actually runs claude-opus-4-8 (variant max, thinking budgetTokens 31999) — this is the on-wire modelID recorded in the message row.
Config modelID ≠ wire modelID → the differentModel branch is taken → providerMetadata (carrying the signature) is dropped on every reasoning part on replay → Anthropic returns 400 on multi-turn.
Persisted error from ~/.local/share/opencode/log/2026-05-31T210903.log:
ERROR service=session.processor session.id=ses_1802048ecffeR3QYgxUDXuFe9t
messageID=msg_e7fe5ad28001Oe2AhSzVeN4Z9l
error=messages.1.content.21: `thinking` or `redacted_thinking` blocks in the
latest assistant message cannot be modified.
at /Applications/OpenCode.app/Contents/Resources/app.asar/out/main/chunks/node-CiOkNpBx.js:197462
at async postToApi (.../node-CiOkNpBx.js:197390)
at async AnthropicMessagesLanguageModel.doStream (.../node-CiOkNpBx.js:347060)
Anthropic response (verbatim, from the persisted error record in SQLite):
"statusCode": 400, "isRetryable": false
"request_id": "req_011CbbLmSxWcMmKJUfhaBTST"
"message": "messages.1.content.21: `thinking` or `redacted_thinking` blocks in
the latest assistant message cannot be modified. These blocks must remain as
they were in the original response."
Single plugin loaded: @ex-machina/opencode-anthropic-auth@1.7.5 (audited: transform.js does not touch reasoning blocks — only system prompt and tool_use block name)
SQLite DB at ~/.local/share/opencode/opencode.db: 850 sessions / 88,166 messages / 340,509 parts / 7.2 GB. Long-term users hit this fast.
Hypothesis on a manual workaround (not yet tested on my end — would appreciate confirmation): explicitly declare the on-wire model (claude-opus-4-8 in my case) in opencode.json with its own variants block, so the config modelID matches the wire modelID and differentModel evaluates to false. Brittle — breaks the next time the wire model auto-bumps — so the proper fix you propose (preserve providerMetadata unconditionally on reasoning parts) is what we need.
Happy to provide more session/log detail if useful.
Activity
github-actions commentedon Apr 16, 2026
This issue might be a duplicate of existing issues. Please check:
fix(opencode): preserve reasoning providerMetadata across model switches
r00tedbrain-backup commentedon May 31, 2026
Confirming this reproduces on the Electron desktop app v1.15.13 (macOS, arm64) — not just the CLI. Your root-cause analysis (
message-v2.tsstrippingproviderMetadatawhendifferentModel === true) matches the symptom exactly.Trigger in my setup:
opencode.jsondeclaresthinkingforclaude-opus-4-6-20250514.claude-opus-4-8(variantmax, thinkingbudgetTokens31999) — this is the on-wiremodelIDrecorded in the message row.differentModelbranch is taken →providerMetadata(carrying the signature) is dropped on every reasoning part on replay → Anthropic returns 400 on multi-turn.Persisted error from
~/.local/share/opencode/log/2026-05-31T210903.log:Anthropic response (verbatim, from the persisted error record in SQLite):
Environment:
app.asar103 MB)@ex-machina/opencode-anthropic-auth@1.7.5(audited:transform.jsdoes not touch reasoning blocks — onlysystemprompt andtool_useblockname)claude-opus-4-8, variantmax, extended thinking enabled (31999 tokens)~/.local/share/opencode/opencode.db: 850 sessions / 88,166 messages / 340,509 parts / 7.2 GB. Long-term users hit this fast.Hypothesis on a manual workaround (not yet tested on my end — would appreciate confirmation): explicitly declare the on-wire model (
claude-opus-4-8in my case) inopencode.jsonwith its own variants block, so the config modelID matches the wire modelID anddifferentModelevaluates tofalse. Brittle — breaks the next time the wire model auto-bumps — so the proper fix you propose (preserveproviderMetadataunconditionally on reasoning parts) is what we need.Happy to provide more session/log detail if useful.
rekram1-node commentedon May 31, 2026
The diagnosis is wrong that's not the issue, if you can send me a session where this happened I can debug it.
r00tedbrain-backup commentedon Jun 1, 2026
Apologies for the wall of text — fair feedback, won't repeat it. Removing the attached gist. Glad to see the fix is already underway.
rekram1-node commentedon Jun 1, 2026
Plz dont send giant ai generated things i dont have time to read it
github-actions commentedon Aug 2, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.