fix(opencode): filter empty text content blocks for all providers#17742
fix(opencode): filter empty text content blocks for all providers#17742RhoninSeiei wants to merge 7 commits into
Conversation
|
The following comment was made by an LLM, it may be inaccurate: Based on the search results, I found the following potentially related PRs: Related/Duplicate PRs Found:
Most likely duplicates: PR #17396 and PR #17712 appear to be addressing the same or very closely related issues. You should verify if this PR (#17742) is redundant with those existing work items. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
|
👋 Thanks for this PR @RhoninSeiei! This directly addresses an issue I've been experiencing. Real-world impactI encountered this exact problem using AWS Bedrock via LiteLLM proxy ( Database analysis results:
Evidence this affects openai-compatible:The existing filter in if (model.api.npm === "@ai-sdk/anthropic" || model.api.npm === "@ai-sdk/amazon-bedrock")But my setup uses Why this PR is importantYour two-part fix is exactly right:
The Recovery aspectI've created a Python repair script to fix already-corrupted databases and opened issue #19309 to track the recovery/repair aspect. This PR prevents future corruption but doesn't fix existing corrupted databases. Both solutions are needed for complete coverage. Offer to helpI'm happy to:
Let me know if there's anything I can do to help get this merged! Environment:
|
…tabase Empty text and reasoning parts with blank or whitespace-only text can be stored in the database during streaming (from text-start events that receive no deltas, or when streams are interrupted). These empty parts cause permanent ValidationException errors with providers like AWS Bedrock, especially when using LiteLLM proxy (@ai-sdk/openai-compatible). This fix adds defensive filtering at two levels: 1. When hydrating parts from database (filters on load) 2. When converting to model messages (filters during conversion) Both filters use .trim() to catch whitespace-only content. Fixes anomalyco#19309 Complements anomalyco#17742 (prevention) with recovery for existing corruption
…tabase Empty text and reasoning parts with blank or whitespace-only text can be stored in the database during streaming (from text-start events that receive no deltas, or when streams are interrupted). These empty parts cause permanent ValidationException errors with providers like AWS Bedrock, especially when using LiteLLM proxy (@ai-sdk/openai-compatible). This fix adds defensive filtering at two levels: 1. When hydrating parts from database (filters on load) 2. When converting to model messages (filters during conversion) Both filters use .trim() to catch whitespace-only content. Fixes anomalyco#19309 Complements anomalyco#17742 (prevention) with recovery for existing corruption
|
Hey @RhoninSeiei — we've been working on the same bug independently (our closed PR was #17565) and wanted to flag something we discovered. Your The problem: Anthropic's adaptive thinking emits whitespace-only text parts between reasoning blocks with cryptographic signatures. The signatures are positionally sensitive — removing the empty/whitespace text part changes the block arrangement and invalidates them. The API then rejects with:
The fix we landed on: Skip the empty/whitespace filter for assistant messages that contain reasoning parts: ```typescript This preserves the signature-sensitive block arrangement while still filtering empty content from user, tool, system, and assistant-without-reasoning messages. PR #16750 independently arrived at the same approach from the #16748 investigation. You might want to coordinate with that PR to avoid conflicting fixes. Hope this helps — your root cause analysis in the PR description is excellent and matches what we found. |
|
@erichasinternet Thanks for the concrete Bedrock-via-LiteLLM confirmation. That proxy path was one of the main targets for this PR, so the real-world validation is helpful. |
|
@robinmordasiewicz Good catch. The adaptive-thinking replay case from #16748 does apply here, so I pushed ef378fa to preserve assistant reasoning separators for Anthropic 4.6-style messages and added a regression test for it. Checks are rerunning now. |
|
Built and tested this on 1.3.13 (dev branch + cherry-pick). Working fix — sessions no longer break with the whitespace text error. I had AI clean up old sessions in the DB to fix resume. Here's a gist with AI step-by-step build-from-source instructions if anyone else needs to apply this before it's released: https://gist.github.com/glassdimly/0988483d5de6a09b20e34df7d1d71afe |
061f0a2 to
c090a62
Compare
Many providers (Anthropic, Bedrock, and proxies like openai-compatible forwarding to Bedrock) reject messages with empty text content blocks. The existing filter only applied to @ai-sdk/anthropic and @ai-sdk/amazon-bedrock, but users connecting through @ai-sdk/openai-compatible (e.g. custom Bedrock proxies, Databricks) hit the same ValidationException in multi-turn conversations. Changes: - normalizeMessages: apply empty text/reasoning filtering universally instead of only for Anthropic/Bedrock providers. Also use .trim() to catch whitespace-only content. - message-v2.ts: skip empty text and reasoning parts at the source when constructing UIMessages from stored parts. - Update test to verify universal filtering for openai-compatible. Fixes anomalyco#15715 Fixes anomalyco#5028 Refs anomalyco#2655
55a3388 to
2c52aeb
Compare
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
Issue for this PR
Fixes #15715
Fixes #5028
Refs #2655
Type of change
What does this PR do?
The existing empty-content filter in
normalizeMessages(transform.ts) only runs for@ai-sdk/anthropicand@ai-sdk/amazon-bedrock. Users who connect through@ai-sdk/openai-compatible(custom Bedrock proxies, Databricks-hosted Claude, etc.) hit the same BedrockValidationException: messages: text content blocks must be non-emptybecause the filter does not cover their provider.The root cause: during multi-turn conversations with tool calls, the streaming processor can create text parts with empty strings (from
text-startevents that receive no deltas). These empty parts propagate through the message pipeline and eventually reach the SDK converter, which sendscontent: ""to Bedrock.This PR makes three changes:
transform.ts- Apply empty text/reasoning filtering universally to all providers (not just Anthropic/Bedrock). Uses.trim()to also catch whitespace-only content. Empty text blocks are never useful for any provider.message-v2.ts- Skip empty/whitespace-only text and reasoning parts at the source when constructing UIMessages, preventing them from entering the pipeline at all.transform.test.ts- Replace the old "does not filter for non-anthropic providers" test with a new test that verifies universal filtering works for@ai-sdk/openai-compatibleproviders.Note: the SDK itself also has a related issue where
convertToOpenAICompatibleChatMessagesoutputscontent: ""instead ofcontent: nullfor assistant messages with only tool calls. A separate issue has been filed at vercel/ai#13466.How did you verify your code works?
bun test test/provider/transform.test.ts) - all 115 tests pass with 220 expect() calls.@ai-sdk/openai-compatible. TheValidationExceptionno longer occurs after 5+ rounds of tool-call-heavy conversation.Screenshots / recordings
N/A - backend logic change, no UI impact.
Checklist