openclaw - 💡(How to fix) Fix Bug: Transcript hygiene does not strip thinking blocks with invalid (non-empty) signatures, causing API rejection loop

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…

OpenClaw's Anthropic transcript hygiene strips thinking blocks with missing, empty, or blank replay signatures before sending conversation history to the API. However, it does not strip thinking blocks with non-empty but cryptographically invalid signatures. Anthropic rejects these with a hard error, and because the session is not cleared, the agent enters a retry loop — repeatedly sending the same buffered response and the same rejection.


Error Message

OpenClaw's Anthropic transcript hygiene strips thinking blocks with missing, empty, or blank replay signatures before sending conversation history to the API. However, it does not strip thinking blocks with non-empty but cryptographically invalid signatures. Anthropic rejects these with a hard error, and because the session is not cleared, the agent enters a retry loop — repeatedly sending the same buffered response and the same rejection. Error returned:

Root Cause

OpenClaw's Anthropic transcript hygiene strips thinking blocks with missing, empty, or blank replay signatures before sending conversation history to the API. However, it does not strip thinking blocks with non-empty but cryptographically invalid signatures. Anthropic rejects these with a hard error, and because the session is not cleared, the agent enters a retry loop — repeatedly sending the same buffered response and the same rejection.

Fix Action

Workaround

Manually strip all thinking blocks from the session JSONL transcript file:

import json, pathlib, shutil

f = pathlib.Path("~/.openclaw/agents/<agent-id>/sessions/<session-id>.jsonl").expanduser()
shutil.copy2(f, f.with_suffix(".jsonl.bak"))
lines = f.read_text().strip().split("\n")
cleaned = []
for line in lines:
    try:
        d = json.loads(line)
        msg = d.get("message", {})
        content = msg.get("content", [])
        if isinstance(content, list):
            msg["content"] = [b for b in content if not (isinstance(b, dict) and b.get("type") == "thinking")]
            d["message"] = msg
        cleaned.append(json.dumps(d))
    except:
        cleaned.append(line)
f.write_text("\n".join(cleaned))

This unblocks the session immediately.


Code Example

LLM request rejected: messages.5.content.33: Invalid `signature` in `thinking` block

---

import json, pathlib, shutil

f = pathlib.Path("~/.openclaw/agents/<agent-id>/sessions/<session-id>.jsonl").expanduser()
shutil.copy2(f, f.with_suffix(".jsonl.bak"))
lines = f.read_text().strip().split("\n")
cleaned = []
for line in lines:
    try:
        d = json.loads(line)
        msg = d.get("message", {})
        content = msg.get("content", [])
        if isinstance(content, list):
            msg["content"] = [b for b in content if not (isinstance(b, dict) and b.get("type") == "thinking")]
            d["message"] = msg
        cleaned.append(json.dumps(d))
    except:
        cleaned.append(line)
f.write_text("\n".join(cleaned))
RAW_BUFFERClick to expand / collapse

Bug: Transcript hygiene does not strip thinking blocks with invalid (non-empty) signatures, causing API rejection loops

OpenClaw version: 2026.5.28 (e932160)
Provider: anthropic/claude-sonnet-4-6 (direct API)
Thinking level: adaptive (Sonnet 4.6 default)


Summary

OpenClaw's Anthropic transcript hygiene strips thinking blocks with missing, empty, or blank replay signatures before sending conversation history to the API. However, it does not strip thinking blocks with non-empty but cryptographically invalid signatures. Anthropic rejects these with a hard error, and because the session is not cleared, the agent enters a retry loop — repeatedly sending the same buffered response and the same rejection.


Steps to reproduce

  1. Run a long multi-turn session with an agent on anthropic/claude-sonnet-4-6 with adaptive (or any) thinking enabled.
  2. Allow the session to accumulate enough turns that early thinking blocks' signatures become invalid from Anthropic's perspective.
  3. Send a new message. Anthropic rejects the entire API request.

Error returned:

LLM request rejected: messages.5.content.33: Invalid `signature` in `thinking` block

The message index (messages.5.content.33) points to a thinking block deep in the replayed conversation history — not the current turn.


Expected behavior

Transcript hygiene for Anthropic should strip any thinking block whose signature Anthropic would reject, including:

  • Missing / null signature ✅ (already handled)
  • Empty / blank signature ✅ (already handled)
  • Non-empty but invalid/expired signature ❌ (not handled — this bug)

Per Anthropic's API contract, thinking block signatures are only valid within the request cycle they were issued. They must not be replayed in subsequent turns as-is — they should either be stripped or passed through verbatim only when Anthropic considers them valid for that session continuation.


Actual behavior

The thinking block with the invalid signature is passed through to Anthropic unchanged. Anthropic returns a 400-level rejection. OpenClaw retries and the agent delivers its buffered response, but the session remains broken — the next turn will fail the same way.


Workaround

Manually strip all thinking blocks from the session JSONL transcript file:

import json, pathlib, shutil

f = pathlib.Path("~/.openclaw/agents/<agent-id>/sessions/<session-id>.jsonl").expanduser()
shutil.copy2(f, f.with_suffix(".jsonl.bak"))
lines = f.read_text().strip().split("\n")
cleaned = []
for line in lines:
    try:
        d = json.loads(line)
        msg = d.get("message", {})
        content = msg.get("content", [])
        if isinstance(content, list):
            msg["content"] = [b for b in content if not (isinstance(b, dict) and b.get("type") == "thinking")]
            d["message"] = msg
        cleaned.append(json.dumps(d))
    except:
        cleaned.append(line)
f.write_text("\n".join(cleaned))

This unblocks the session immediately.


Suggested fix

In src/agents/transcript-policy.ts (Anthropic provider section), extend the thinking block signature check to treat any signature value that Anthropic would reject as invalid — not just null/empty/blank. The safest approach may be to strip all thinking blocks from turns beyond the immediately preceding assistant turn, which is consistent with Anthropic's documented intent for thinking block replay.

From the transcript hygiene docs:

"Thinking blocks with missing, empty, or blank replay signatures are stripped before provider conversion."

The condition should be broadened to also strip blocks where the signature exists but is not valid for replay.


Impact

  • Any long-running persistent session with an Anthropic Claude 4.6 model using adaptive thinking is vulnerable.
  • The session enters a silent retry loop that delivers duplicate messages to the end user.
  • No automatic recovery — manual transcript surgery is required.

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

Transcript hygiene for Anthropic should strip any thinking block whose signature Anthropic would reject, including:

  • Missing / null signature ✅ (already handled)
  • Empty / blank signature ✅ (already handled)
  • Non-empty but invalid/expired signature ❌ (not handled — this bug)

Per Anthropic's API contract, thinking block signatures are only valid within the request cycle they were issued. They must not be replayed in subsequent turns as-is — they should either be stripped or passed through verbatim only when Anthropic considers them valid for that session continuation.


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 Bug: Transcript hygiene does not strip thinking blocks with invalid (non-empty) signatures, causing API rejection loop