Skip to content

fix(agents): concatenate signature_delta chunks in transport stream#87582

Merged
steipete merged 2 commits into
openclaw:mainfrom
Jerry-Xin:fix/thinking-block-storage
May 28, 2026
Merged

fix(agents): concatenate signature_delta chunks in transport stream#87582
steipete merged 2 commits into
openclaw:mainfrom
Jerry-Xin:fix/thinking-block-storage

Conversation

@Jerry-Xin

@Jerry-Xin Jerry-Xin commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the root cause of permanent session corruption when using Anthropic models with extended thinking enabled.

The anthropic-transport-stream.ts (used by the embedded agent runner) was overwriting thinkingSignature on each signature_delta event instead of appending. Since Anthropic sends the thinking block signature across multiple streaming chunks, only the last chunk survived. The truncated signature was persisted to session JSONL, and all subsequent replay attempts failed with HTTP 400.

Root Cause

// BEFORE (bug): overwrites on each delta chunk
block.thinkingSignature = delta.signature;

// AFTER (fix): concatenates all delta chunks
block.thinkingSignature = (block.thinkingSignature || "") + delta.signature;

The correct implementation already existed in the same repo at src/llm/providers/anthropic.ts:629-634 — this fix aligns the transport stream with that reference.

Changes

  • src/agents/anthropic-transport-stream.ts — One-line fix: append signature deltas instead of overwriting
  • src/agents/anthropic-transport-stream.test.ts — Updated 2 existing assertions + added dedicated multi-chunk concatenation test
  • test/proof/thinking-signature-real-proof.ts — Real-scenario proof exercising the patched code path + live API replay validation

Real behavior proof

  • Behavior or issue addressed: When OpenClaw stores assistant messages with Anthropic thinking blocks, the signature field is truncated (only last signature_delta chunk kept). On replay, Anthropic rejects the invalid signature with HTTP 400, permanently bricking the session. Fixes Bug: anthropic-transport-stream overwrites signature_delta instead of appending, causing permanent session corruption #87574.

  • Real environment tested: macOS arm64, OpenClaw 0.6.0-dev gateway, Claude Sonnet 4 via Anthropic API proxy, Node.js v22.22.1. The patched code path section exercises createAnthropicMessagesTransportStreamFn directly with crafted multi-chunk SSE events. The live API section uses real Anthropic streaming with thinking.type: "enabled".

  • Exact steps or command run after this patch:

cd /tmp/openclaw-src
git checkout fix/thinking-block-storage
export OPENAI_API_KEY="<api-key>"
export ANTHROPIC_BASE_URL="<anthropic-compatible-endpoint>"
node --import tsx test/proof/thinking-signature-real-proof.ts
  • Evidence after fix:

Terminal output from the real proof script:

=== Patched Code Path Proof: createAnthropicMessagesTransportStreamFn ===

  This section exercises the ACTUAL patched transport stream function
  with a mocked fetch serving crafted SSE events.

  Calling createAnthropicMessagesTransportStreamFn()...
  SSE payload: 5 signature_delta events

  ✅ PASS: Transport stream produced a thinking block
  ✅ PASS: Transport stream produced a text block
  ✅ PASS: Thinking text captured correctly (55 chars)
  ✅ PASS: Signature equals concatenation of all 5 chunks (75 chars)
  ✅ PASS: Signature is NOT just the last chunk (old overwrite bug would produce "rX4uVnB9oI6mKpL")
  ✅ PASS: Signature length 75 === sum of chunk lengths 75
  ✅ PASS: All signature chunks appear in order (verifies append, not prepend/shuffle)
  ✅ PASS: Text block content preserved correctly

--- Patched Code Path: 8 passed, 0 failed ---

=== Live API Replay Proof: Thinking Block Signature Integrity ===

API Base: <anthropic-compatible-endpoint>
Model: claude-sonnet-4-6

Step 1: Sending request with extended thinking enabled...
Step 2: Parsing SSE stream...

Step 3: Validation Results

--- Event Summary ---
  message_start: 1
  content_block_start: 2
  content_block_delta: 1766
  content_block_stop: 2
  message_delta: 1
  message_stop: 1

--- Signature Delta Analysis ---
  Total signature_delta events: 1
  Chunk 1: 432 chars "Er8CCl0IDhABGAIqQBXVqq4oYahHsQ..."

--- Signature Comparison ---
  Full signature (concatenated): 432 chars
  Overwrite-only (OLD BUG):      432 chars
  Thinking text length:          127 chars

--- Assertions ---
  ✅ PASS: At least one signature_delta event received
  ℹ️  INFO: Only 1 signature_delta chunk received (proxy/Bedrock may coalesce chunks)
         Multi-chunk concatenation verified by patched code path section above.
  ✅ PASS: Signature captured successfully (432 chars)
  ✅ PASS: Thinking text was captured from thinking_delta events

  ℹ️  Single-chunk delivery — simulating truncation for negative proof

--- Replay Validation ---
  Sending replay request with the captured signature...
  ✅ PASS: Replay with CORRECT (concatenated) signature succeeds: HTTP 200
  Sending replay with TRUNCATED (old bug) signature...
  ✅ PASS: Replay with TRUNCATED (old bug) signature fails: HTTP 400 — confirms the bug causes API rejection

--- Live API Replay: 5 passed, 0 failed ---

=== OVERALL PROOF SUMMARY ===
  Patched code path: 8 passed, 0 failed
  Live API replay:   5 passed, 0 failed
  Total:             13 passed, 0 failed

✅ PROOF PASSED — signature concatenation fix verified
  • Observed result after fix: The proof demonstrates three critical behaviors:

    1. Patched code path proof: The actual createAnthropicMessagesTransportStreamFn function is called with 5 crafted signature_delta SSE events. The output thinkingSignature equals the concatenation of all 5 chunks (75 chars), not just the last chunk (15 chars). This would FAIL without the patch (old behavior would produce only "rX4uVnB9oI6mKpL").
    2. Positive replay proof: A thinking block signature captured from real Anthropic streaming replays successfully (HTTP 200).
    3. Negative replay proof: A truncated signature (simulating the old overwrite bug) is rejected by Anthropic with HTTP 400 — confirming this is the exact mechanism that permanently bricked sessions.
  • What was not tested: Multi-chunk signature_delta delivery was not observed from the live API endpoint (AWS Bedrock proxy coalesces chunks). Multi-chunk concatenation through the actual patched function is verified by the patched code path section which feeds 5 separate signature_delta events directly into createAnthropicMessagesTransportStreamFn.

Related Issues

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 28, 2026
@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed May 28, 2026, 11:24 AM ET / 15:24 UTC.

Summary
This PR changes the embedded Anthropic Messages transport stream to reset and append signature_delta chunks per content block, adds regression tests, and adds a live proof script for thinking-signature replay.

PR surface: Source +6, Tests +666. Total +672 across 3 files.

Reproducibility: yes. Current main overwrites repeated signature_delta values, and the PR body supplies after-fix terminal proof showing concatenation plus live replay success with a valid signature and failure with a truncated signature.

Review metrics: 1 noteworthy metric.

  • Persisted replay field changed: 1 changed. thinkingSignature is written to session history and replayed to Anthropic, so parser semantics affect existing and future session continuity.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Wait for the in-progress latest-head checks to finish before merge.
  • Have a maintainer explicitly accept the seeded-signature reset plus append behavior for persisted Anthropic replay.

Risk before merge

  • [P1] The parser changes a persisted replay field: the first signature_delta now clears any seeded content-block signature before appending later chunks, so maintainers should explicitly accept that contract for direct and compatible Anthropic streams.
  • [P1] Several latest-head check runs were still in progress at review time, so final merge should wait for required checks to complete.

Maintainer options:

  1. Accept the replay parser contract after checks finish (recommended)
    Maintainers can land this once latest-head checks are green and they accept the first-delta reset plus append behavior for Anthropic-compatible streams.
  2. Require another final-head live replay proof
    If maintainers want stronger upgrade confidence, ask for a final-head run of the proof script against the same live provider path before merge.
  3. Pause for broader replay policy review
    If the seeded-signature semantics are still uncertain, pause this PR and decide the direct-Anthropic versus compatible-proxy replay policy first.

Next step before merge

  • [P2] Maintainer review should decide the replay compatibility contract and wait for final checks; there is no narrow automated repair to request from this PR branch.

Security
Cleared: No concrete security or supply-chain issue was found; the diff changes parser logic, tests, and a proof script without dependency, workflow, package, permission, or secret-storage changes.

Review details

Best possible solution:

Land the narrow parser and regression-test change after required checks finish and maintainers accept the persisted Anthropic thinking-signature replay contract; keep broader recovery or sanitization work in the linked follow-up issues.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main overwrites repeated signature_delta values, and the PR body supplies after-fix terminal proof showing concatenation plus live replay success with a valid signature and failure with a truncated signature.

Is this the best way to solve the issue?

Yes, with maintainer acceptance of the replay contract. The change is narrow, aligns the embedded transport with the sibling Anthropic provider's append semantics, and adds focused seeded-signature and multi-delta coverage.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 5216841a9e59.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal proof for the patched transport path and live API replay, including successful valid-signature replay and rejected truncated-signature replay.

Label justifications:

  • P1: The PR targets an agent session replay corruption path that can break Anthropic thinking sessions for real users.
  • merge-risk: 🚨 compatibility: The PR changes how Anthropic-compatible streamed signatures are serialized when start signatures and later deltas appear.
  • merge-risk: 🚨 session-state: The changed thinkingSignature value is persisted into assistant history and replayed on later turns.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes after-fix terminal proof for the patched transport path and live API replay, including successful valid-signature replay and rejected truncated-signature replay.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal proof for the patched transport path and live API replay, including successful valid-signature replay and rejected truncated-signature replay.
Evidence reviewed

PR surface:

Source +6, Tests +666. Total +672 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 7 1 +6
Tests 2 667 1 +666
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 674 2 +672

What I checked:

  • Repository policy read and applied: Root AGENTS.md was read fully, and its session-state compatibility guidance affects this PR because the changed field is persisted and replayed. (AGENTS.md:22, 5216841a9e59)
  • Scoped agents policy read: The scoped agents guide was read; it did not add a line-level defect, but it reinforces keeping agent tests focused through dependency injection. (src/agents/AGENTS.md:1, 5216841a9e59)
  • Current main overwrites signature deltas: Current main assigns block.thinkingSignature = delta.signature for each Anthropic signature_delta, so later chunks replace earlier chunks. (src/agents/anthropic-transport-stream.ts:1294, 5216841a9e59)
  • Persisted replay surface confirmed: The stored thinkingSignature is sent back as Anthropic thinking.signature, making this a session replay field rather than display-only state. (src/agents/anthropic-transport-stream.ts:401, 5216841a9e59)
  • Sibling provider appends signature deltas: The sibling Anthropic provider initializes the signature string and appends event.delta.signature, supporting the PR's accumulation direction. (src/llm/providers/anthropic.ts:633, 5216841a9e59)
  • Dependency contract checked: The bundled Anthropic SDK version defines streamed SignatureDelta as a signature: string content-block delta variant, matching the parser branch under review. (package.json:1821, 5216841a9e59)

Likely related people:

  • joshavant: Recent GitHub history shows signed Anthropic thinking payload work touching the same transport/provider files shortly before this PR. (role: recent area contributor; confidence: high; commits: 4a45a259ec1e; files: src/agents/anthropic-transport-stream.ts, src/agents/anthropic-transport-stream.test.ts, src/llm/providers/anthropic.ts)
  • steipete: Recent history includes the internal agent runtime refactor across this surface, and the PR head includes a seeded-signature follow-up commit by this person. (role: major refactor and follow-up contributor; confidence: medium; commits: bb46b79d3c14, ffdb947b4f22; files: src/agents/anthropic-transport-stream.ts, src/llm/providers/anthropic.ts)
  • vincentkoc: Local blame and GitHub history both point to prior work on Anthropic transport tests and runtime behavior in this area. (role: adjacent transport/runtime contributor; confidence: medium; commits: 54c9820ed9c2, 6835f05cd03c; files: src/agents/anthropic-transport-stream.ts, src/agents/anthropic-transport-stream.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@Jerry-Xin
Jerry-Xin force-pushed the fix/thinking-block-storage branch 3 times, most recently from 4d7f5ad to 2136e52 Compare May 28, 2026 08:30
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 28, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels May 28, 2026
@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg: ✨ hatched 🌱 uncommon Tiny Clawlet. Rarity: 🌱 uncommon. Trait: hums during re-review.

Details

Share on X: post this hatch
Copy: My PR egg hatched a 🌱 uncommon Tiny Clawlet in ClawSweeper.
Hatchability:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

About:

  • Eggs appear after real-behavior proof passes. They are collectible flavor only.
  • Review momentum changes the shell state: follow-up work warms it, re-review makes it wobble, and a clean final review lets it hatch.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@Jerry-Xin
Jerry-Xin force-pushed the fix/thinking-block-storage branch from 2136e52 to cb39899 Compare May 28, 2026 09:09
@Jerry-Xin

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 28, 2026
@Jerry-Xin
Jerry-Xin force-pushed the fix/thinking-block-storage branch from cb39899 to 39ba28e Compare May 28, 2026 12:26
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@Jerry-Xin
Jerry-Xin force-pushed the fix/thinking-block-storage branch from ce20c59 to 4046173 Compare May 28, 2026 14:11
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels May 28, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@steipete
steipete force-pushed the fix/thinking-block-storage branch from b0bac55 to 5162834 Compare May 28, 2026 14:28
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels May 28, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels May 28, 2026
Jerry-Xin and others added 2 commits May 28, 2026 16:17
The anthropic-transport-stream was overwriting thinkingSignature on each
signature_delta event instead of appending. Since Anthropic sends the
thinking block signature across multiple streaming chunks, only the last
chunk survived. The truncated signature was persisted to session JSONL,
causing all subsequent replay attempts to fail with HTTP 400:

  thinking or redacted_thinking blocks in the latest assistant message
  cannot be modified

This permanently bricked sessions with no user recovery path.

Fix: accumulate signature_delta values by concatenating instead of
overwriting, matching the correct implementation in the LLM provider
layer (src/llm/providers/anthropic.ts:629-634).

Includes real-scenario proof against live Anthropic API validating that
correct signatures replay successfully while truncated signatures are
rejected.

Fixes openclaw#87574
Refs openclaw#80625, openclaw#85781, openclaw#87475
@steipete
steipete force-pushed the fix/thinking-block-storage branch from 6a13ca1 to ffdb947 Compare May 28, 2026 15:18
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@steipete
steipete merged commit f8c8c0d into openclaw:main May 28, 2026
111 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via rebase onto main.

Thanks @Jerry-Xin!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: anthropic-transport-stream overwrites signature_delta instead of appending, causing permanent session corruption

2 participants