fix(provider): drop whitespace-only text blocks for Anthropic/Bedrock#31376
fix(provider): drop whitespace-only text blocks for Anthropic/Bedrock#31376Tushar49 wants to merge 1 commit into
Conversation
GitHub Copilot Claude models (routed through the Anthropic /v1/messages
endpoint via @ai-sdk/anthropic) returned HTTP 400 "messages: text content
blocks must contain non-whitespace text" whenever the conversation history
contained an assistant turn whose only text block was whitespace (e.g. a
single space). This commonly happens when a cheap fallback model returns an
empty/whitespace response that is later replayed to Claude, and it makes the
affected models unusable mid-conversation even though they are listed and
selectable.
ProviderTransform.message already strips empty ("") text/reasoning parts for
Anthropic, but the text guard only matched exactly "" and let whitespace-only
blocks (" ", "\n", ...) through, while the reasoning guard right below already
used .trim(). Treat whitespace-only text the same as empty.
A single space is also deliberately emitted by MessageV2.toModelMessages as a
separator between *signed* reasoning blocks, so preserve whitespace-only text
only when the message still carries surviving signed/redacted Anthropic
reasoning (the AI SDK merges that separator into an adjacent block). Bedrock
never emits that separator, so whitespace-only text is dropped unconditionally
there.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
The following comment was made by an LLM, it may be inaccurate: The search results show PR #31045 (
PR #31045 operates at a different layer (session message replay) and only filters exact empty strings, while PR #31376 targets the provider transform layer and catches whitespace-only blocks (including single spaces). No duplicate PRs found |
|
u may not have seen my comment, plz send me a session u encountered this in: |
Issue for this PR
Closes #31259
Type of change
What does this PR do?
Copilot's Claude models go through the Anthropic
/v1/messagesAPI, which 400s on any text block that is empty or only whitespace.ProviderTransform.messagealready drops empty text parts for Anthropic, but it checkspart.text !== "", so a lone" "slips through and the whole request fails. The reasoning check right beside it already uses.trim()— I just make the text check match.The one space I keep is the separator
MessageV2.toModelMessagesinserts between signed reasoning blocks; it's only kept when the message still has signed Anthropic reasoning (the SDK merges it into a neighbouring block so it never hits the API on its own). Bedrock has the same API limits but no separator, so I trim it there unconditionally.Not a dup of #31045 — that drops empty parts earlier in message-v2 but only for exact
"". It doesn't catch a" "that's already saved in a session, which is what my logs actually hit.How did you verify your code works?
bun test packages/opencode/test/provider/transform.test.ts. Added 5 cases: the exact failing message, mixed empty + real text, the signed-reasoning separator (kept), an unsigned-reasoning case (dropped, no leak), and Bedrock. All 14 in the "anthropic empty content filtering" suite pass;oxlintclean on the 2 changed files.Screenshots / recordings
n/a — no UI change.
Checklist