Skip to content

fix: filter empty-content assistant messages before API call#248

Merged
jalehman merged 1 commit into
Martian-Engineering:mainfrom
wujiaming88:fix/filter-empty-assistant-messages
Apr 3, 2026
Merged

fix: filter empty-content assistant messages before API call#248
jalehman merged 1 commit into
Martian-Engineering:mainfrom
wujiaming88:fix/filter-empty-assistant-messages

Conversation

@wujiaming88

Copy link
Copy Markdown
Contributor

Problem

Anthropic API rejects messages with empty content:

The content field in the Message object at messages.0 is empty.
Add a ContentBlock object to the content field and try again.

This occurs when:

  1. Tool-use-only assistant turns are stored with content='' and zero message_parts in the database
  2. filterNonFreshAssistantToolCalls() strips all tool_use blocks from a non-fresh assistant message, leaving content: []

Root Cause

assembler.ts assemble() passes rawMessages directly to sanitizeToolUseResultPairing() and then to the API without validating that assistant messages have non-empty content.

While filterNonFreshAssistantToolCalls() does skip messages where content.length === 0 after filtering, this only covers the array case. Messages with falsy string content ('') bypass this check via the !Array.isArray() branch and are passed through unchanged.

Fix

Add an explicit filter in assemble() before the return statement that removes assistant messages with empty content (either [] or falsy string). Only targets assistant role — user messages are left untouched.

const cleaned = rawMessages.filter(
  (m) =>
    !(
      m?.role === 'assistant' &&
      (Array.isArray(m.content) ? m.content.length === 0 : !m.content)
    ),
);

Testing

Verified on production instance with 5,246+ affected messages across 12 conversations. Filter correctly removes empty assistant messages while preserving all valid messages. All agents recovered immediately after deploying this fix.

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 jalehman merged commit 612e95b into Martian-Engineering:main Apr 3, 2026
1 check passed
@jalehman

jalehman commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants