Skip to content

fix: use non-empty placeholder in dropThinkingBlocks to prevent Bedrock ValidationException#71623

Closed
wujiaming88 wants to merge 1 commit into
openclaw:mainfrom
wujiaming88:fix/empty-thinking-placeholder
Closed

fix: use non-empty placeholder in dropThinkingBlocks to prevent Bedrock ValidationException#71623
wujiaming88 wants to merge 1 commit into
openclaw:mainfrom
wujiaming88:fix/empty-thinking-placeholder

Conversation

@wujiaming88

Copy link
Copy Markdown
Contributor

Problem

When dropThinkingBlocks strips all thinking content from an assistant message, it replaces the content with a synthetic text block to preserve turn structure. Previously this placeholder used an empty string:

{ type: "text", text: "" }

However, both Bedrock's convertMessages and Anthropic's convertAnthropicMessages (in @mariozechner/pi-ai) filter out text blocks where text.trim().length === 0:

// Bedrock convertMessages
case "text":
    if (c.text.trim().length === 0) continue; // ← filters empty placeholder

// Anthropic convertAnthropicMessages  
if (block.type === "text") {
    if (block.text.trim().length > 0) blocks.push({...}); // ← skips empty
    continue;
}

This causes the assistant message's content array to become empty after filtering, which triggers a Bedrock ValidationException:

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

Reproduction

  1. Start a conversation with extended thinking enabled on Bedrock (e.g. claude-opus-4-6)
  2. Have a multi-turn conversation where an assistant response contains only thinking blocks (no text, no tool calls)
  3. Continue the conversation so dropThinkingBlocks runs on that historical message
  4. The next API call fails with the validation error above

Observed in production

[agent/embedded] embedded run agent end: isError=true
  model=global.anthropic.claude-opus-4-6-v1 provider=amazon-bedrock
  error=Validation error: The content field in the Message object at messages.16 is empty.

Fix

Use "[thinking]" as the placeholder text so it survives downstream empty-text filters across all providers.

Changes

  • src/agents/pi-embedded-runner/thinking.ts — Replace empty string placeholder with "[thinking]" and add a named constant with documentation
  • src/agents/pi-embedded-runner/thinking.test.ts — Update test expectation to match new placeholder

…ck ValidationException

When dropThinkingBlocks strips all thinking content from an assistant
message, it replaces the content with a synthetic text block to preserve
turn structure. Previously this placeholder used an empty string:

  { type: "text", text: "" }

However, both Bedrock's convertMessages and Anthropic's
convertAnthropicMessages filter out text blocks where
text.trim().length === 0. This causes the assistant message's content
array to become empty, which triggers a Bedrock ValidationException:

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

This was observed in production when an assistant message contained only
thinking blocks (no tool calls or visible text). After dropThinkingBlocks
ran, the empty placeholder was filtered by the provider converter,
leaving an empty content array that Bedrock rejected.

Fix: use "[thinking]" as the placeholder text so it survives downstream
empty-text filters across all providers.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Apr 25, 2026
@greptile-apps

greptile-apps Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a production ValidationException on Bedrock caused by dropThinkingBlocks leaving an empty content array after stripping thinking-only assistant messages. The fix replaces the empty-string placeholder with the non-empty string "[thinking]" (captured in a well-documented named constant), which survives the text.trim().length === 0 filters applied by both Bedrock's convertMessages and Anthropic's convertAnthropicMessages. The test is updated to match the new expectation.

Confidence Score: 5/5

Safe to merge — minimal, well-targeted fix that resolves a confirmed production error with updated tests.

The change is a one-line value swap backed by thorough JSDoc explaining the invariant, a named constant to prevent future regressions, and an updated unit test. No unrelated code is touched, and the fix directly addresses the root cause observed in production logs.

No files require special attention.

Reviews (1): Last reviewed commit: "fix: use non-empty placeholder in dropTh..." | Re-trigger Greptile

@steipete

Copy link
Copy Markdown
Contributor

Thanks for the report and patch. I verified the underlying issue against current @mariozechner/pi-ai: blank text placeholders are filtered by the Anthropic and Bedrock adapters, so stripped thinking-only assistant turns can lose their assistant replay shape.

I landed the fix on main in a018db771d with a slightly broader implementation:

  • replaces stripped thinking-only assistant turns with non-empty [assistant reasoning omitted] text
  • covers both normal dropThinkingBlocks replay and the Anthropic recovery retry path
  • keeps the latest assistant thinking turn untouched for regular Anthropic continuation
  • adds Anthropic + Bedrock sanitize-history regression coverage
  • adds live Anthropic checks for regular replay, omitted-reasoning placeholder replay, and tool replay
  • documents the transcript hygiene behavior

Validation run locally before push:

  • pnpm test src/logging/diagnostic-stability.test.ts src/agents/pi-embedded-runner/thinking.test.ts src/agents/pi-embedded-runner.sanitize-session-history.test.ts
  • OPENCLAW_LIVE_TEST=1 ANTHROPIC_LIVE_TEST=1 pnpm test:live -- src/agents/pi-embedded-runner.anthropic-tool-replay.live.test.ts
  • pnpm tsgo:core
  • pnpm tsgo:core:test
  • OPENCLAW_LOCAL_CHECK=0 pnpm lint:core
  • pnpm build

Closing this PR as superseded by the landed main fix. Changelog credits @wujiaming88. Thanks again.

@steipete steipete closed this Apr 25, 2026
@wujiaming88 wujiaming88 deleted the fix/empty-thinking-placeholder branch April 26, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants