openclaw - 💡(How to fix) Fix Empty-text ContentBlock persisted to transcript permanently bricks a session (Bedrock validation error: messages.N.content.0 is blank) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

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#72899Fetched 2026-04-28 06:30:38
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
1
Author
Participants
Timeline (top)
subscribed ×1

An empty-text user ContentBlock can be persisted to a session transcript, permanently poisoning the session. Every subsequent LLM call fails with a Bedrock/Anthropic validation error and the session becomes unusable until the transcript is manually edited.

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

Error Message

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

Root Cause

  1. From this point on, every new turn — including a simple @Leo hi — fails immediately at provider-invoke time because Bedrock rejects the blank text block.

Fix Action

Fix / Workaround

Workaround until fix lands

Code Example

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

---

{
  "role": "user",
  "content": [{ "type": "text", "text": "" }]
}

---

# backup, remove the offending line (adjust N), verify, restore in place
cp ~/.openclaw/agents/<agent>/sessions/<id>.jsonl{,.bak}
awk 'NR!=N' ~/.openclaw/agents/<agent>/sessions/<id>.jsonl.bak > ~/.openclaw/agents/<agent>/sessions/<id>.jsonl

---

jq -c 'select(.type=="message") | {idx: input_line_number, role: .message.role, t0: .message.content[0].type, text_len: ((.message.content[0].text // "")|length)}' \
  ~/.openclaw/agents/<agent>/sessions/<id>.jsonl | grep '"text_len":0'
RAW_BUFFERClick to expand / collapse

Summary

An empty-text user ContentBlock can be persisted to a session transcript, permanently poisoning the session. Every subsequent LLM call fails with a Bedrock/Anthropic validation error and the session becomes unusable until the transcript is manually edited.

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

Environment

  • OpenClaw version: 2026.4.24
  • Model: amazon-bedrock/global.anthropic.claude-opus-4-7 (Bedrock Converse streaming)
  • Channel: Discord group session
  • OS: Linux 6.17.0-1010-aws (arm64), Node v22.22.0

Reproduction

  1. Start a Discord group session where the main agent spawns background subagent tasks (ACP — in our case a claude / Claude Code child via sessions_spawn({ runtime: "acp", thread: true })).
  2. Let one subagent task complete (internal "subagent task completed" system event is injected as a user message → handled with NO_REPLY).
  3. Something (see "Likely source" below) causes the runtime to write a user message to the transcript with content:
{
  "role": "user",
  "content": [{ "type": "text", "text": "" }]
}
  1. From this point on, every new turn — including a simple @Leo hi — fails immediately at provider-invoke time because Bedrock rejects the blank text block.

Evidence (redacted transcript around the poisoning event)

Session transcript: ~/.openclaw/agents/main/sessions/<sessionId>.jsonl

Linetimestamprolecontent[0].typetext (preview)
14815:15:29Zassistant(assistant reply)...
14915:16:00Zusertext[... UTC] <<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>> ... subagent task luma-skill-build completed successfully ... <<<END_OPENCLAW_INTERNAL_CONTEXT>>>
15015:16:05ZassistanttextNO_REPLY
15115:16:58Zusertext"" ← poisoned record
15215:16:59Zassistanttext[assistant turn failed before producing content]
15315:20:10ZassistanttextBackground task failed: luma-skill-build (run XXXXXXX). Permission prompt unavailable in non-interactive mode
15415:22:32ZassistanttextBackground task failed: luma-skill-build (run YYYYYYY). Permission prompt unavailable in non-interactive mode

From line 152 onwards, every turn errors out with the messages.N.content.0 is blank validation error. OpenClaw created several .bak-<pid>-<epoch> backups of the transcript but did not self-heal.

Likely source

Based on timestamps, the poisoned record appears immediately after a subagent completion delivery and just before a cascade of subagent-run failures (Permission prompt unavailable in non-interactive mode). Suspect the empty block came from one of:

  • A subagent task-completion / agent-to-agent announce event whose result payload serialized to an empty string.
  • A Discord inbound envelope whose body was empty (e.g. attachment-only or autoscrubbed content).
  • An internal system-event injection with an empty text field.

In any case, the ingestion layer does not validate text-block content before writing to the transcript.

Impact

  • Session hard-fails until manual transcript surgery. The only recovery we found was to delete the offending line from the .jsonl and restart the turn. Users cannot realistically self-recover.
  • High blast radius. Any surface that spawns subagents / handles system events can trip this.
  • Silent. No actionable error surfaced to the user — only the cryptic Bedrock validation string on the next reply attempt.

Suggested fix

  1. Guard at ingestion. When receiving any message (user, tool-result, system-event injection), drop or coerce empty text ContentBlocks before persistence:
    • Drop blocks where block.type === "text" && (block.text ?? "").trim().length === 0.
    • If that would leave content: [], either drop the whole message or substitute a single-block placeholder like "[empty message]".
  2. Guard at send-time (defense-in-depth). In the Bedrock / Anthropic adapter that builds the provider request, filter out empty text blocks as a last-mile safety net so a legacy poisoned transcript cannot brick a session.
  3. Self-heal on retry. When the provider returns this specific validation error, consider scrubbing the offending block in-memory (and optionally emitting a warning) so the user isn't stuck.

Workaround until fix lands

# backup, remove the offending line (adjust N), verify, restore in place
cp ~/.openclaw/agents/<agent>/sessions/<id>.jsonl{,.bak}
awk 'NR!=N' ~/.openclaw/agents/<agent>/sessions/<id>.jsonl.bak > ~/.openclaw/agents/<agent>/sessions/<id>.jsonl

To find N:

jq -c 'select(.type=="message") | {idx: input_line_number, role: .message.role, t0: .message.content[0].type, text_len: ((.message.content[0].text // "")|length)}' \
  ~/.openclaw/agents/<agent>/sessions/<id>.jsonl | grep '"text_len":0'

extent analysis

TL;DR

To fix the issue, guard against empty text ContentBlocks at ingestion time and consider adding a defense-in-depth check at send-time to prevent Bedrock validation errors.

Guidance

  • Implement a check at the ingestion layer to drop or coerce empty text ContentBlocks before persistence, ensuring that block.type === "text" && (block.text ?? "").trim().length === 0 conditions are handled.
  • Add a defense-in-depth check in the Bedrock / Anthropic adapter to filter out empty text blocks before sending the provider request.
  • Consider implementing self-healing on retry by scrubbing the offending block in-memory when the provider returns the specific validation error.

Example

No code snippet is provided as the issue suggests changes to the ingestion layer and Bedrock / Anthropic adapter, which are not explicitly defined in the issue.

Notes

The suggested fix and workaround assume that the ingestion layer and Bedrock / Anthropic adapter can be modified to add the necessary checks. The workaround provided using awk and jq can be used to manually remove the offending line from the transcript until a fix is implemented.

Recommendation

Apply the suggested fix by guarding against empty text ContentBlocks at ingestion time and adding a defense-in-depth check at send-time, as this will prevent Bedrock validation errors and allow sessions to recover from poisoned transcripts.

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…

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-text ContentBlock persisted to transcript permanently bricks a session (Bedrock validation error: messages.N.content.0 is blank) [1 participants]