Repository navigation
Description
Summary
Manual /compact always fails on first attempt with the following error when the session uses a model with extended thinking enabled:
undefined: The model returned the following errors:
messages.N.content.M: `thinking` or `redacted_thinking` blocks in the latest assistant
message cannot be modified. These blocks must remain as they were in the original response.
A second compaction on the now-empty session succeeds. The first one never does.
Environment
- opencode version:
1.16.2 - Provider: Amazon Bedrock (
@ai-sdk/amazon-bedrock) - Model:
claude-sonnet-4-6—maxvariant (enables extended thinking / budget tokens) - Trigger: Manual
/compacton any session where the last assistant turn producedthinkingblocks
Root Cause
In packages/opencode/src/session/compaction.ts, processCompaction calls:
const modelMessages = yield* MessageV2.toModelMessagesEffect(msgs, model, {
stripMedia: true,
toolOutputMaxChars: TOOL_OUTPUT_MAX_CHARS,
})Inside toModelMessagesEffect (message-v2.ts), each assistant message is checked against the compaction model:
const differentModel =
`${model.providerID}/${model.id}` !== `${msg.info.providerID}/${msg.info.modelID}`Because the session model is amazon-bedrock/claude-sonnet-4-6 (max) and the compaction agent typically resolves to a different model/provider, differentModel = true is almost always the case. This triggers the branch that strips providerMetadata from every part:
assistantMessage.parts.push({
type: "text",
text,
...(differentModel ? {} : { providerMetadata: part.metadata }), // stripped when differentModel
})For Anthropic models with extended thinking, providerMetadata on reasoning parts contains the cryptographic signature Anthropic requires to validate the block hasn't been tampered with. Stripping it causes the API to reject the message as "modified."
The error targets the latest assistant message in the array because that is the only position where Anthropic enforces signature validation.
Why the Second Compaction Succeeds
After the first failure, the new empty session has no prior assistant messages with thinking blocks — nothing to trigger the validation error.
Proposed Fix
Compaction models don't need internal reasoning content to summarize a conversation. The cleanest fix is a stripReasoning option in toModelMessagesEffect that skips reasoning parts entirely when building messages for compaction.
packages/opencode/src/session/message-v2.ts:
options?: {
stripMedia?: boolean
toolOutputMaxChars?: number
+ stripReasoning?: boolean
}
// inside the assistant message parts loop:
+if (options?.stripReasoning && part.type === "reasoning") continuepackages/opencode/src/session/compaction.ts:
const modelMessages = yield* MessageV2.toModelMessagesEffect(msgs, model, {
stripMedia: true,
toolOutputMaxChars: TOOL_OUTPUT_MAX_CHARS,
+ stripReasoning: true,
})Note: The same option should also be passed to the
estimate()call insideselect(), since that path also invokestoModelMessagesEffect.
Edge case: if an assistant message consists only of reasoning parts (no text, no tool calls), stripping them yields an empty parts array. The existing empty-message guard should handle this, but worth verifying.
Activity
github-actions commentedon Jun 7, 2026
This issue might be a duplicate of existing issues. Please check:
thinking/redacted_thinkingblocks cannot be modified during compaction on Amazon Bedrock with Opus 4.6 / extended thinking enabledtoModelMessagesEffectwhere reasoning/provider metadata is not properly stripped during compactiontiffanychum commentedon Jun 7, 2026
I'd like to work on this. Root cause:
toModelMessagesEffectstripsproviderMetadatafrom reasoning parts when the compaction model differs from the session model, removing the Anthropic cryptographic signature. Fix: add astripReasoningoption that skips reasoning parts entirely during compaction. Changes inmessage-v2.tsandcompaction.ts. PR coming shortly.Skeptomenos commentedon Jun 23, 2026
Same error on 1.17.4, but via the opposite branch —
differentModel === false(same-model compaction)Confirming this still reproduces on 1.17.4 (Amazon Bedrock), but through a different path than the issue body describes. The current write-up only covers
differentModel === true; the same-model case is also broken and I think it's worth documenting here.Repro:
amazon-bedrock/claude-opus-4-8,xhighvariant (extended thinking on).compactionagent configured, so the compaction model resolves to the triggering message's model:/compact→ 400 from Bedrockconverse-stream:claude-sonnet-4-6→ succeeds.Why it fails on the same-model path: because the compaction model == the model that produced the history,
differentModel === false, sotoModelMessagesEffectreplays the stored reasoning parts as realreasoning/thinking blocks with their originalbedrock.signature. The replayedselected.headends on an assistant turn, so a thinking block lands in the "latest assistant message", and after the reorder/merge into Bedrock Converse format it's no longer the pristine first block (note the error points atcontent.1) → Anthropic rejects it.Why the model-switch retry succeeds (and a correction to the issue body): in 1.17.4 the
differentModel === truebranch no longer strips just the signature — it downgrades the whole reasoning part to plain text:So switching models flips
differentModeltotrue, strips the thinking blocks, and sidesteps the error. The v1.16.2 mechanism in the description (differentModel=true keeping an unsigned thinking block) appears fixed, but thedifferentModel === false/ same-model case is still broken.This is more evidence that the proposed
stripReasoning: trueoption for compaction is the right fix regardless of branch — reasoning content isn't needed to summarize the conversation, and stripping it unconditionally avoids both variants.(Confirmed by inspecting the shipped 1.17.4 build plus the stored session messages; the failing request had the thinking block at
content[1]of the final assistant message, produced by the same Opus model used for compaction.)github-actions commentedon Aug 23, 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.