Skip to content

fix(app-bundle): drop messages with empty content array before API dispatch - #1218

Merged
jeonghun-jj-lee merged 1 commit into
mainfrom
fix/empty-content-guard
Sep 15, 2026
Merged

jeonghun-jj-lee merged 1 commit into
mainfrom
fix/empty-content-guard

Conversation

@jeonghun-jj-lee

Copy link
Copy Markdown
Contributor

Problem

Anthropic/Bedrock APIs reject messages whose content field is an empty array:

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

Surfaced by Opus 4.8 on Bedrock, which produces reasoning-heavy message patterns that the existing provider-specific filters don't fully retain.

Root cause

The empty-content filters in normalizeMessages (Anthropic at L166, Bedrock at L196) catch most cases, but edge cases slip through:

  • Reasoning-only assistant turns whose parts are all stripped
  • Cross-provider history replay where signature keys mismatch (anthropic vs bedrock providerOptions)
  • The interleaved-reasoning code path that returns early after filtering

The gap exists in the upstream opencode base (7fe99387); no Amicode commit introduced it.

Fix

A provider-agnostic final guard at the end of message() in transform.ts — the last transform before the return — that drops any message with content: []:

msgs = msgs.filter((msg) => !Array.isArray(msg.content) || msg.content.length > 0)

One line, catches every upstream path at once.

Verification

  • Transform tests: 407/407 (incl. 5 new regression tests)
  • Extension suite: 3572/3572 green
  • Binary: compiles, smoke test passes
  • Manifest hashes updated

…spatch

Anthropic/Bedrock APIs reject messages whose content field is an empty array.
The provider-specific filters in normalizeMessages (Anthropic at L166,
Bedrock at L196) catch most cases, but edge cases slip through — reasoning-
only assistant turns whose parts are all stripped, cross-provider history
replay where signature keys mismatch (anthropic vs bedrock providerOptions),
or the interleaved-reasoning code path that returns early after filtering.

Add a provider-agnostic final guard at the end of the message() export that
drops any message with content: [] before it reaches the SDK. This is the
last transform before the return, so it catches every upstream path at once.

Surfaced by Opus 4.8 on Bedrock, which produces reasoning-heavy message
patterns that the existing Bedrock filter's signature check did not retain.
The upstream gap exists in the pinned base (7fe99387); this overlay patch
is the minimal defensive fix.

Verification: transform 407/407 (incl. 5 new), extension 3572/3572 green;
build:binary compiles and smoke test passes; manifest hashes updated.
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 3535d878-c657-4f28-bbea-0b54827b2b71

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jeonghun-jj-lee
jeonghun-jj-lee merged commit e28013f into main Sep 15, 2026
11 checks passed
@jeonghun-jj-lee
jeonghun-jj-lee deleted the fix/empty-content-guard branch September 15, 2026 20:16
jeonghun-jj-lee added a commit that referenced this pull request Sep 16, 2026
…he wave-2 merge (#1236)

The wave-2 back-merge of main into dev (dfe3fbe) hand-ported the 568306f
reasoning->text conversion but dropped the separate #1218 final guard from
provider/transform.ts:

    msgs = msgs.filter((msg) => !Array.isArray(msg.content) || msg.content.length > 0)

This is the last line of defense against a message with content: [] reaching a
provider SDK/API — the class of Bedrock session-wedge #1218 closed. dev kept
main's transform.test.ts (the 'final empty content guard' describe block still
asserts empty-content messages are dropped), so the implementation and its
tests were out of sync on dev. The overlay's bun tests aren't run by any CI job
(build-binary compiles the overlay but doesn't test it; 'fast' is the extension
vitest), so the regression was silent — dev is CI-green with the guard gone.

Restores the guard verbatim from main: transform.ts is now byte-identical to
origin/main except Aaron's wave-2 provenance comment. manifest.json updated
surgically (the single transform.ts content hash); drift_gate.mjs PASS.

Co-authored-by: amicode-ci <ci@amicode.local>
jack-champagne added a commit that referenced this pull request Sep 16, 2026
fix(engine): port the #1218 empty-content guard onto main (currently only on dev)
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.

1 participant