Skip to content

fix(provider): preserve assistant message content when reasoning blocks present#21370

Open
edevil wants to merge 1 commit intoanomalyco:devfrom
edevil:fix/preserve-thinking-block-signatures
Open

fix(provider): preserve assistant message content when reasoning blocks present#21370
edevil wants to merge 1 commit intoanomalyco:devfrom
edevil:fix/preserve-thinking-block-signatures

Conversation

@edevil
Copy link
Copy Markdown
Contributor

@edevil edevil commented Apr 7, 2026

Issue for this PR

Closes #16748

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

normalizeMessages() in transform.ts unconditionally filters out empty text parts from all message roles, including assistant. When Anthropic adaptive thinking (Opus 4.6, Sonnet 4.6) emits a whitespace-only text part between two reasoning blocks, processor.ts trims it to "", and then normalizeMessages removes it. This shifts the block arrangement from [thinking, text, thinking, text, tool_use] to [thinking, thinking, text, tool_use], invalidating the positionally-sensitive cryptographic signatures on the thinking blocks. The Anthropic API then rejects with: "thinking blocks in the latest assistant message cannot be modified".

The fix is one line: skip the empty-text filter for assistant messages that contain reasoning blocks. These messages must be replayed verbatim since thinking block signatures encode positional context. Assistant messages without reasoning blocks (e.g. plain text responses, compaction summaries) continue to be filtered normally.

if (msg.role === "assistant" && msg.content.some((part) => part.type === "reasoning")) return msg

The empty-text filter was added on Jan 5 (c285304a) before adaptive thinking existed (Feb 13, 0d90a22f9), so it was correct at the time. The bug is an emergent interaction with Opus 4.6's signature-sensitive thinking blocks.

How did you verify your code works?

  • All 123 transform tests pass (0 failures)
  • Typecheck passes across all 13 packages
  • New test reproduces the exact issue scenario: [reasoning(sig1), text(""), reasoning(sig2), text("answer"), tool_use] — asserts all 5 parts are preserved
  • 3 existing tests updated to match the corrected behavior
  • 1 new test added for non-assistant message filtering (regression guard)

Screenshots / recordings

N/A — backend logic change, no UI impact.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…ks present

normalizeMessages() unconditionally filtered empty text parts from all
message roles, including assistant. When Anthropic adaptive thinking
(Opus 4.6, Sonnet 4.6) emits an empty text part between two reasoning
blocks, removing it shifts thinking block positions and invalidates
the cryptographic signatures, causing the API to reject with:
'thinking blocks in the latest assistant message cannot be modified'

Skip the empty-text filter for assistant messages that contain reasoning
blocks. These messages must be replayed verbatim since thinking block
signatures encode positional context. Assistant messages without
reasoning blocks continue to be filtered normally.

Closes anomalyco#16748
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

The following comment was made by an LLM, it may be inaccurate:

Potential Duplicate Found:

This PR appears to be directly related or a potential duplicate. It addresses the same issue (#16748) with the same root cause (empty-text filtering in normalizeMessages affecting assistant messages with reasoning blocks). Both PRs target the same problem with Anthropic's adaptive thinking signatures.

@edevil
Copy link
Copy Markdown
Contributor Author

edevil commented Apr 7, 2026

It seems this error message predates adaptive thinking so it must be something else.

@edevil edevil closed this Apr 7, 2026
@edevil edevil reopened this Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

normalizeMessages() removes empty text parts between reasoning blocks, invalidating Anthropic thinking block signatures

1 participant