fix: prevent empty assistant content from crashing provider APIs#238
Closed
wujiaming88 wants to merge 1 commit into
Closed
fix: prevent empty assistant content from crashing provider APIs#238wujiaming88 wants to merge 1 commit into
wujiaming88 wants to merge 1 commit into
Conversation
contentFromParts() returned [] when both message parts and fallback content were empty for assistant messages. Providers like Anthropic reject messages with zero ContentBlock entries. Two fixes: 1. contentFromParts() now always returns at least one text block for assistant role instead of an empty array. 2. assemble() filters out assistant messages with empty content arrays as a safety net after tool-call orphan filtering.
wujiaming88
pushed a commit
to wujiaming88/lossless-claw
that referenced
this pull request
Apr 3, 2026
When tool-use-only assistant turns are stored with content='' and zero message_parts, or when filterNonFreshAssistantToolCalls strips all tool_use blocks from a non-fresh assistant message, the resulting content array is empty ([]) or the content string is falsy. Anthropic (and other providers) reject messages with empty content: 'The content field in the Message object at messages.0 is empty' Add an explicit filter in assemble() to remove these empty assistant messages before passing to sanitizeToolUseResultPairing and the API. The filter only targets assistant messages — user messages with empty content are left untouched (provider may handle differently). Closes Martian-Engineering#238
jalehman
pushed a commit
that referenced
this pull request
Apr 3, 2026
When tool-use-only assistant turns are stored with content='' and zero message_parts, or when filterNonFreshAssistantToolCalls strips all tool_use blocks from a non-fresh assistant message, the resulting content array is empty ([]) or the content string is falsy. Anthropic (and other providers) reject messages with empty content: 'The content field in the Message object at messages.0 is empty' Add an explicit filter in assemble() to remove these empty assistant messages before passing to sanitizeToolUseResultPairing and the API. The filter only targets assistant messages — user messages with empty content are left untouched (provider may handle differently). Closes #238 Co-authored-by: wujiaming88 <wujiaming88@example.com>
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
When LCM assembles context from the SQLite database, assistant messages can end up with an empty
contentarray ([]). This causes provider APIs like Anthropic Claude to reject the request with:Root Cause
In
src/assembler.ts,contentFromParts()returns[]when:parts.length === 0)""is falsy)This can happen when:
Additionally,
filterNonFreshAssistantToolCalls()can strip all tool-call blocks from a non-fresh assistant message, leavingcontent: [], but the empty-check only skips messages where the filtering itself produced an empty array — messages that started empty pass through.Fix
Two defensive layers:
contentFromParts(): Always return at least one text block for assistant messages instead of an empty array:assemble()safety net: Filter out any assistant messages that still have empty content arrays before returning, catching edge cases from tool-call orphan filtering:Testing