Repository navigation
🩹 fix: Drop Null/Empty Reasoning Blocks in the v1 Bedrock Converter - #309
Conversation
Apply the LibreChat-AI#248 null/empty-text guard to convertFromV1ToChatBedrockConverseMessage: drop `reasoning` blocks whose `reasoning` is null/empty and `reasoning_content` blocks that fail isSerializableBedrockReasoningBlock, and fall back to the empty-turn placeholder when the drop empties the turn — instead of sending `reasoningText: { text: null }`, which Bedrock Converse rejects with `Member must not be null`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 541bbe3822
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d0c84a0939
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b7ca42e95
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c390f0240a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Problem
#248 guarded the native-Bedrock converter (
convertAIMessageToConverseMessage) against reasoning blocks whosereasoningText.textis null/empty, but the v1 converter (convertFromV1ToChatBedrockConverseMessage, taken whenresponse_metadata.output_version === 'v1') still serializes reasoning unguarded in both of its branches:A v1
reasoningblock whosereasoningis null/empty — e.g. produced by a model that omits reasoning text by default (thinking.display: "omitted", the default on Claude Opus 4.7+ / Sonnet 5) — reaches Bedrock asreasoningText: { text: null }and the whole request is rejected:We hit exactly this in a LibreChat deployment: Bedrock
anthropic.claude-sonnet-5+ MCP tools fails on the tool-call replay turn with the error above. It is the same failure class #248 fixed — #248's "Scope / remaining gap" section covers the non-streaming@langchain/awsdelegation but not this v1 path in the same file, which is still unguarded onmain/ 3.2.63.Fix
Apply #248's drop-the-unsendable-block approach to the v1 converter:
reasoning(v1 standard block): drop whenreasoningis null/empty.reasoning_content: gate on the existingisSerializableBedrockReasoningBlockbefore converting, same as the non-v1 path.BEDROCK_EMPTY_TEXT_PLACEHOLDERfallback (from 🩹 fix: Drop Foreign Reasoning Blocks in Anthropic Message Converter #243) when the drop leaves a turn with no content blocks and no tool calls — mirroring the non-v1 converter, so the drop can't trade the null-text 400 for an empty-content 400.Blocks carrying real text (or redacted content) are converted unchanged.
Change Type
Testing
Test-first: added a
convertToConverseMessages — v1 reasoning serializationdescribe block tomessage_inputs.test.ts(5 cases; 4 fail without the fix, reproducingreasoningText: { text: undefined }passing through):reasoningblock with missingreasoning→ dropped; sibling text + tool calls survivereasoningblock with emptyreasoning→ droppedreasoning_contentblock → droppedreasoning/reasoning_contentblocks carrying real text → still converted (not over-dropped)Test Configuration:
npx jest src/llm/bedrock: 9 suites / 106 passed, 4 skipped, 0 failednpx jest src/llm src/messages: only failures are 4 pre-existing env-dependent suites (invoke,google,anthropic,vertexai) that fail identically on a cleanmaincheckout without this changenpx tsc --noEmit,npx eslint,npx prettier --checkon changed files: cleanChecklist
🤖 Generated with Claude Code