Bug Description
When a long-running session (e.g., Discord channel) accumulates many messages and goes through compaction, orphaned tool_result blocks can be sent to the Anthropic API without their matching tool_use. This causes a 400 invalid_request_error that permanently bricks the session — the bot can never respond again in that channel.
Root Cause
Observed in a Discord channel session (6,724 JSONL entries, 23 compactions):
- An assistant request is aborted mid-tool-call (
stopReason: "aborted")
- The
toolCall block and its toolResult message are both recorded in the JSONL
- When compaction runs, the conversation is summarized — but the
toolResult entry survives into the post-compaction message window while its matching toolCall does not
- On the next API call, Anthropic receives a
tool_result referencing a tool_use_id that doesn't exist in the conversation
- API returns:
messages.166.content.1: unexpected tool_use_id found in tool_result blocks
- Every subsequent API call fails with the same error — the session is bricked
Reproduction
This happened naturally in a Discord channel session after ~2 weeks of continuous use. The JSONL had:
- Line 6716: Assistant message with
toolCall (stopReason: "aborted")
- Line 6717:
toolResult message referencing that tool call
- Line 6719: Compaction entry (parentId chain includes both 6716 and 6717)
- Line 6721: Next assistant message → 400 error
Expected Behavior
Compaction should ensure tool_use/tool_result pairs are always kept or removed together. If a toolCall is summarized away, its corresponding toolResult must also be excluded from the API request.
Suggested Fix
When building the API message array from the JSONL:
- After compaction, validate that every
tool_result has a matching tool_use in the message array
- Strip orphaned
tool_result blocks before sending to the API
- Consider also handling orphaned
tool_use blocks (tool_use without a following tool_result)
Alternatively, during compaction:
- Ensure aborted tool calls and their results are either both kept or both summarized
- Never split a tool_use/tool_result pair across a compaction boundary
Workaround
Manually clearing the session transcript (.jsonl file) restores functionality but loses all conversation history.
Environment
- OpenClaw: 2026.2.22-2
- Provider: Anthropic (Claude Opus 4.6)
- Session type: Discord channel (long-running, continuous)
- OS: macOS 15.7.3
Bug Description
When a long-running session (e.g., Discord channel) accumulates many messages and goes through compaction, orphaned
tool_resultblocks can be sent to the Anthropic API without their matchingtool_use. This causes a400 invalid_request_errorthat permanently bricks the session — the bot can never respond again in that channel.Root Cause
Observed in a Discord channel session (6,724 JSONL entries, 23 compactions):
stopReason: "aborted")toolCallblock and itstoolResultmessage are both recorded in the JSONLtoolResultentry survives into the post-compaction message window while its matchingtoolCalldoes nottool_resultreferencing atool_use_idthat doesn't exist in the conversationmessages.166.content.1: unexpected tool_use_id found in tool_result blocksReproduction
This happened naturally in a Discord channel session after ~2 weeks of continuous use. The JSONL had:
toolCall(stopReason: "aborted")toolResultmessage referencing that tool callExpected Behavior
Compaction should ensure tool_use/tool_result pairs are always kept or removed together. If a
toolCallis summarized away, its correspondingtoolResultmust also be excluded from the API request.Suggested Fix
When building the API message array from the JSONL:
tool_resulthas a matchingtool_usein the message arraytool_resultblocks before sending to the APItool_useblocks (tool_use without a following tool_result)Alternatively, during compaction:
Workaround
Manually clearing the session transcript (
.jsonlfile) restores functionality but loses all conversation history.Environment