openclaw - 💡(How to fix) Fix Empty-response fallback leaks provider validation errors to users (self-propagating loop) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#73020Fetched 2026-04-28 06:28:31
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Participants
Timeline (top)
closed ×1commented ×1mentioned ×1subscribed ×1

When a primary model returns an empty response (0 output tokens) and the runtime falls over to a fallback provider that validates content blocks strictly (e.g., Anthropic on Bedrock), the fallback call fails with a validation error. That error string is then surfaced as the assistant's reply to the user and stored in the session transcript as the assistant turn, creating a self-propagating loop where every subsequent turn reproduces the same error.

Error Message

15:21:57 [agent/embedded] empty response detected: runId=9022ecc8 sessionId=e249bbc1 provider=google/gemini-3-flash-preview — retrying 1/1 with visible-answer continuation 15:22:26 [agent/embedded] empty response retries exhausted: attempts=1/1 — surfacing incomplete-turn error 15:22:26 [model-fallback/decision] decision=candidate_failed requested=google/gemini-3-flash-preview candidate=google/gemini-3-flash-preview reason=format next=amazon-bedrock/global.anthropic.claude-opus-4-6-v1 15:22:54 [agent/embedded] embedded run agent end: isError=true model=global.anthropic.claude-opus-4-6-v1 provider=amazon-bedrock error=Validation error: The text field in the ContentBlock object at messages.3.content.0 is blank.

Root Cause

Root cause (two compounding bugs)

Fix Action

Fix / Workaround

In the model-fallback orchestration (around pi-embedded-xwfWu_QR.js emptyResponseRetryAttempts and downstream fallback dispatch), before calling the next candidate:

Workaround applied locally

Code Example

15:21:57 [agent/embedded] empty response detected: runId=9022ecc8 sessionId=e249bbc1 
         provider=google/gemini-3-flash-preview — retrying 1/1 with visible-answer continuation
15:22:26 [agent/embedded] empty response retries exhausted: attempts=1/1 
         — surfacing incomplete-turn error
15:22:26 [model-fallback/decision] decision=candidate_failed 
         requested=google/gemini-3-flash-preview candidate=google/gemini-3-flash-preview 
         reason=format next=amazon-bedrock/global.anthropic.claude-opus-4-6-v1
15:22:54 [agent/embedded] embedded run agent end: isError=true 
         model=global.anthropic.claude-opus-4-6-v1 provider=amazon-bedrock 
         error=Validation error: The text field in the ContentBlock object at messages.3.content.0 is blank.

---

{"role":"assistant","content":[{"type":"text","text":""}],"provider":"google","model":"gemini-3-flash-preview","usage":{"input":39627,"output":0,"totalTokens":39627},"stopReason":"stop"}

---

// Sanitize history: drop empty assistant text blocks so Bedrock validators don't reject.
function sanitizeHistoryForFallback(messages) {
  return messages.map(m => {
    if (m.role !== "assistant" || !Array.isArray(m.content)) return m;
    const cleaned = m.content.filter(b => b.type !== "text" || (b.text && b.text.trim()));
    return cleaned.length > 0 ? { ...m, content: cleaned } : null;
  }).filter(Boolean);
}
RAW_BUFFERClick to expand / collapse

Bug: Empty-response fallback path leaks provider validation errors to users

Summary

When a primary model returns an empty response (0 output tokens) and the runtime falls over to a fallback provider that validates content blocks strictly (e.g., Anthropic on Bedrock), the fallback call fails with a validation error. That error string is then surfaced as the assistant's reply to the user and stored in the session transcript as the assistant turn, creating a self-propagating loop where every subsequent turn reproduces the same error.

Environment

  • OpenClaw 2026.4.25 (aa36ee6)
  • macOS 15.x, node v25.5.0
  • Channel: Discord (DM)
  • Primary model on session: amazon-bedrock/global.anthropic.claude-opus-4-7
  • Fallback chain: claude-opus-4-6-v1claude-sonnet-4-6google/gemini-3-flash-preview

Symptom

User sees assistant replies like:

Validation error: The text field in the ContentBlock object at messages.2.content.0 is blank. Add text to the text field, and try again.

…repeatedly across multiple turns in the same session.

Log evidence

15:21:57 [agent/embedded] empty response detected: runId=9022ecc8 sessionId=e249bbc1 
         provider=google/gemini-3-flash-preview — retrying 1/1 with visible-answer continuation
15:22:26 [agent/embedded] empty response retries exhausted: attempts=1/1 
         — surfacing incomplete-turn error
15:22:26 [model-fallback/decision] decision=candidate_failed 
         requested=google/gemini-3-flash-preview candidate=google/gemini-3-flash-preview 
         reason=format next=amazon-bedrock/global.anthropic.claude-opus-4-6-v1
15:22:54 [agent/embedded] embedded run agent end: isError=true 
         model=global.anthropic.claude-opus-4-6-v1 provider=amazon-bedrock 
         error=Validation error: The text field in the ContentBlock object at messages.3.content.0 is blank.

Session transcript snapshot

Assistant message stored with content: [{"type":"text","text":""}] and stopReason: "stop" from the Gemini run. Subsequent Bedrock fallback receives that empty block in history and rejects.

{"role":"assistant","content":[{"type":"text","text":""}],"provider":"google","model":"gemini-3-flash-preview","usage":{"input":39627,"output":0,"totalTokens":39627},"stopReason":"stop"}

Root cause (two compounding bugs)

  1. Empty-response path records empty assistant block. pi-embedded (around maxEmptyResponseRetryAttempts / emptyResponseRetryInstruction logic) stores the empty Gemini output in session history before failing over.
  2. No sanitization before fallback call. When the runtime falls over to a different provider, the empty assistant block is included in the conversation history sent to the fallback. Bedrock Converse strictly rejects empty text ContentBlocks.
  3. Provider validation errors are surfaced as message bodies. The rejection error from Bedrock gets delivered to the Discord channel as the assistant reply, and is stored as the next assistant turn — so every subsequent turn re-reproduces the same failure.

Expected behavior

  • Strip (or replace with a single-space/"[no content]" placeholder) empty assistant text blocks in conversation history before making a fallback provider call.
  • Never surface raw provider validation errors as user-facing message bodies. Either retry with sanitized history, escalate to an internal error (logged, not delivered), or deliver a generic apology string.
  • Do not persist a failed fallback's error message as the assistant turn in the session transcript — the original empty assistant turn should be replaced/invalidated once a successful model responds, or dropped entirely if all fallbacks fail.

Reproducer

  1. Configure any session with primary = Bedrock Anthropic, last fallback = google/gemini-3-flash-preview.
  2. Induce Gemini Flash to return 0 output tokens (e.g., supply an input that triggers its refusal-without-text mode, or use an input that includes only system content and no user text).
  3. Observe: retry-with-continuation also returns empty → runtime falls over to Bedrock → Bedrock returns the ContentBlock blank error → error appears as assistant reply in the channel and in the session transcript.

Suggested fix

In the model-fallback orchestration (around pi-embedded-xwfWu_QR.js emptyResponseRetryAttempts and downstream fallback dispatch), before calling the next candidate:

// Sanitize history: drop empty assistant text blocks so Bedrock validators don't reject.
function sanitizeHistoryForFallback(messages) {
  return messages.map(m => {
    if (m.role !== "assistant" || !Array.isArray(m.content)) return m;
    const cleaned = m.content.filter(b => b.type !== "text" || (b.text && b.text.trim()));
    return cleaned.length > 0 ? { ...m, content: cleaned } : null;
  }).filter(Boolean);
}

And in the delivery path: if the final fallback returns a provider validation error (4xx shape), replace the user-facing body with a generic "[internal error — retry]" message and log the raw error to the gateway error log only.

Workaround applied locally

  • Removed google/gemini-3-flash-preview from agents.defaults.model.fallbacks (now Opus 4.7 → Opus 4.6 → Sonnet 4.6 only).
  • Deleted corrupted DM session transcript to stop the self-propagating error loop.

Cheers.

extent analysis

TL;DR

Sanitize conversation history by removing empty assistant text blocks before making a fallback provider call to prevent validation errors from being surfaced as user-facing message bodies.

Guidance

  • Identify and modify the model-fallback orchestration code to sanitize conversation history before calling the next candidate, using a function like sanitizeHistoryForFallback to drop empty assistant text blocks.
  • Update the delivery path to replace user-facing bodies with a generic error message when a final fallback returns a provider validation error, and log the raw error to the gateway error log.
  • Consider removing or reordering fallback providers that are causing validation errors, as a temporary workaround.
  • Review and test the emptyResponseRetryAttempts and emptyResponseRetryInstruction logic to ensure it is correctly handling empty responses and retries.

Example

function sanitizeHistoryForFallback(messages) {
  return messages.map(m => {
    if (m.role !== "assistant" || !Array.isArray(m.content)) return m;
    const cleaned = m.content.filter(b => b.type !== "text" || (b.text && b.text.trim()));
    return cleaned.length > 0 ? { ...m, content: cleaned } : null;
  }).filter(Boolean);
}

Notes

This solution assumes that the sanitizeHistoryForFallback function is correctly implemented and that the delivery path is updated to handle provider validation errors. Additional testing and verification may be necessary to ensure the fix is working as expected.

Recommendation

Apply the suggested fix by sanitizing conversation history and updating the delivery path to handle provider validation errors, as this should prevent the self-propagating error loop and provide a better user experience.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

  • Strip (or replace with a single-space/"[no content]" placeholder) empty assistant text blocks in conversation history before making a fallback provider call.
  • Never surface raw provider validation errors as user-facing message bodies. Either retry with sanitized history, escalate to an internal error (logged, not delivered), or deliver a generic apology string.
  • Do not persist a failed fallback's error message as the assistant turn in the session transcript — the original empty assistant turn should be replaced/invalidated once a successful model responds, or dropped entirely if all fallbacks fail.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Empty-response fallback leaks provider validation errors to users (self-propagating loop) [1 comments, 2 participants]