fix(api): normalize content field format before sending to provider - #67767
Open
jelloeater-agent wants to merge 1 commit into
Open
fix(api): normalize content field format before sending to provider#67767jelloeater-agent wants to merge 1 commit into
jelloeater-agent wants to merge 1 commit into
Conversation
Bifrost and OpenRouter strictly validate the message content field,
rejecting assistant messages whose content is neither a string nor
an array of Content block dicts with HTTP 400: 'content field is
neither a string nor an array of Content blocks'.
This was triggered when malformed content (e.g., an array of plain
strings like ["thinking", "text"], or dicts missing the 'type'
key) reached the wire via thinking-only prefill continuation or
message merging.
Fix: add content format normalization to sanitize_api_messages(),
the final pre-API chokepoint that already handles empty tool_calls,
orphaned tool results, and duplicate call IDs. Normalizations:
- Array of plain strings -> Content block array
[{'type':'text','text':'...'}, ...]
- Dicts missing 'type' key -> add type='text' fallback
- Non-string, non-list values -> convert to str
- None/null -> pass through unchanged
Collaborator
teknium1
reviewed
Jul 25, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for targeting strict-provider message validation. The compatibility gap is still actionable: sanitize_api_messages() on current main has no content-shape normalization (agent/agent_runtime_helpers.py:2762-2957).
Problems
- The new pass is not the final request mutation. The main loop calls
_sanitize_api_messages()before_drop_thinking_only_and_merge_users()(agent/conversation_loop.py:1403-1416), so content can still be changed after this normalization. - The diff contains no regression tests. Existing merge coverage only asserts typed-block inputs and outputs (
tests/run_agent/test_thinking_only_sanitizer.py:244-268). - The proposed merge explanation is not reproduced by current code: mixed string/list branches create typed text blocks, while list/list only concatenates its existing operands (
agent/agent_runtime_helpers.py:1352-1369).
Suggested changes
- Normalize after the drop/merge pass in both request paths, or prove later mutations cannot introduce invalid content.
- Add full request-path tests for malformed lists, typeless dicts, scalar/dict values, and valid typed blocks.
Automated hermes-sweeper review.
|
|
||
| # --- Normalize content field formats ──────────────────────────── | ||
| # Bifrost/OpenRouter reject assistant messages whose `content` is | ||
| # neither a string nor an array of Content block dicts (e.g. an |
Contributor
There was a problem hiding this comment.
This is not the final content mutation in the main path: _drop_thinking_only_and_merge_users() runs after this sanitizer at agent/conversation_loop.py:1403-1416. Please place the normalization after that pass (and mirror the summary path), or demonstrate that the later pass cannot create an invalid wire shape.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Bifrost and OpenRouter strictly validate the message
contentfield, rejecting assistant messages whose content is neither a string nor an array of Content block dicts with HTTP 400:"content field is neither a string nor an array of Content blocks".This is triggered when malformed content reaches the wire via:
drop_thinking_only_and_merge_userscan produce content as an array of plain strings instead of proper Content blocksFix
Add content format normalization to
sanitize_api_messages(), the final pre-API chokepoint that already handles emptytool_calls, orphaned tool results, and duplicate call IDs.["hello", "world"](strings array)[{"type":"text","text":"hello"}, {"type":"text","text":"world"}][{"text":"no type"}](dicts missingtype)[{"type":"text","text":"no type"}]{"weird": "format"}(non-string, non-list)str(..)None/ valid string / valid content blocksTesting
All core normalization paths verified:
type→type="text"fallback added