openclaw - ✅(Solved) Fix Fix: tool_use_id mismatch error causes infinite loop instead of recovery [2 pull requests, 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#44473Fetched 2026-04-08 00:46:27
View on GitHub
Comments
0
Participants
1
Timeline
15
Reactions
0
Author
Participants
Timeline (top)
referenced ×8cross-referenced ×7

Error Message

// Handle tool_use/tool_result mismatch errors (transcript corruption) if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(errorText)) { return { payloads: [ { text: "Conversation history corruption detected. " + "Please use /new to start a fresh session.", isError: true, }, ], meta: { durationMs: Date.now() - started, agentMeta: { sessionId: sessionIdUsed, provider, model: model.id, }, systemPromptReport: attempt.systemPromptReport, error: { kind: "tool_result_mismatch", message: errorText }, }, }; }

Root Cause

  1. formatAssistantErrorText() catches the error via the generic invalid_request_error regex
  2. Returns "LLM request rejected: <message>" as user-facing text
  3. No special handling exists to suggest /new or attempt transcript repair
  4. User replies → same broken history → same error → loop

The existing repairToolUseResultPairing() runs on context build but the mismatch persisted, suggesting an edge case in the repair logic or timing.

Fix Action

Fixed

PR fix notes

PR #44481: fix: handle tool_use_id mismatch error to prevent infinite retry loops

Description (problem / solution / changelog)

Fixes #44473

Problem

When tool_use_id mismatch error occurs, OpenClaw enters an infinite retry loop.

Solution

Add pattern matching for tool_use_id/tool_call_id mismatch errors.

Testing

  • Pattern matches error
  • Prevents infinite retry

Changed files

  • src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts (modified, +21/-0)
  • src/agents/pi-embedded-helpers/errors.ts (modified, +4/-1033)

PR #44499: fix: handle tool_use_id mismatch error to prevent infinite retry loops

Description (problem / solution / changelog)

Fixes #44473

Problem

When Anthropic API rejects a request due to tool_use/tool_result mismatch, the error is formatted and sent to user, but no recovery is attempted. When user responds, the same broken conversation history is sent again, causing infinite retry loops.

Solution

  1. Added isToolUseIdMismatchError() helper function
  2. Added detection in formatAssistantErrorText()
  3. Added test coverage for both error formats

Testing

  • Helper function matches 'does not match any tool_use block' pattern
  • Helper function matches 'mismatch' pattern
  • User receives recovery guidance: /reset

Addresses all review feedback from previous attempts.

Changed files

  • pnpm-lock.yaml (modified, +6/-0)
  • src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts (modified, +41/-0)
  • src/agents/pi-embedded-helpers/errors.ts (modified, +29/-1)

Code Example

LLM request rejected: messages.156.content.2: unexpected tool_use_id found in tool_result blocks: toolu_01YMCQha57tDp359ZestPfwU. Each tool_result block must have a corresponding tool_use block in the previous message.

---

// Catch tool_use/tool_result mismatch errors (transcript corruption)
if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(raw)) {
    return ("Conversation history corruption detected. " +
        "Please use /new to start a fresh session.");
}

---

// Handle tool_use/tool_result mismatch errors (transcript corruption)
if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(errorText)) {
    return {
        payloads: [
            {
                text: "Conversation history corruption detected. " +
                    "Please use /new to start a fresh session.",
                isError: true,
            },
        ],
        meta: {
            durationMs: Date.now() - started,
            agentMeta: {
                sessionId: sessionIdUsed,
                provider,
                model: model.id,
            },
            systemPromptReport: attempt.systemPromptReport,
            error: { kind: "tool_result_mismatch", message: errorText },
        },
    };
}
RAW_BUFFERClick to expand / collapse

Bug Description

When the Anthropic API rejects a request due to a tool_use/tool_result mismatch error:

LLM request rejected: messages.156.content.2: unexpected tool_use_id found in tool_result blocks: toolu_01YMCQha57tDp359ZestPfwU. Each tool_result block must have a corresponding tool_use block in the previous message.

The error is formatted and sent to the user as a reply, but no recovery is attempted. When the user responds, the same broken conversation history is sent again, causing the same error — resulting in an infinite loop of the same error message.

Root Cause

  1. formatAssistantErrorText() catches the error via the generic invalid_request_error regex
  2. Returns "LLM request rejected: <message>" as user-facing text
  3. No special handling exists to suggest /new or attempt transcript repair
  4. User replies → same broken history → same error → loop

The existing repairToolUseResultPairing() runs on context build but the mismatch persisted, suggesting an edge case in the repair logic or timing.

Proposed Fix

Add detection for tool_use/tool_result mismatch errors alongside the existing role ordering error handling:

1. In errors.js - formatAssistantErrorText() (after line ~255):

// Catch tool_use/tool_result mismatch errors (transcript corruption)
if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(raw)) {
    return ("Conversation history corruption detected. " +
        "Please use /new to start a fresh session.");
}

2. In errors.js - sanitizeUserFacingText() (after role ordering check):

Same pattern added for consistency.

3. In run.js - promptError handler (after role ordering check ~line 333):

// Handle tool_use/tool_result mismatch errors (transcript corruption)
if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(errorText)) {
    return {
        payloads: [
            {
                text: "Conversation history corruption detected. " +
                    "Please use /new to start a fresh session.",
                isError: true,
            },
        ],
        meta: {
            durationMs: Date.now() - started,
            agentMeta: {
                sessionId: sessionIdUsed,
                provider,
                model: model.id,
            },
            systemPromptReport: attempt.systemPromptReport,
            error: { kind: "tool_result_mismatch", message: errorText },
        },
    };
}

Impact

  • Before: User sees repeated raw API error, no way to escape except manually running /new
  • After: User sees actionable message on first occurrence, loop is broken

Additional Notes

A more robust fix would be to attempt automatic transcript repair when this error is detected (calling repairToolUseResultPairing() and retrying), but the user-facing message is a safe fallback that prevents the loop.

extent analysis

Fix Plan

To address the tool_use/tool_result mismatch error and prevent an infinite loop of error messages, we will implement the following steps:

  • Modify the formatAssistantErrorText() function in errors.js to catch tool_use/tool_result mismatch errors and return a user-facing message suggesting the use of /new to start a fresh session.
  • Update the sanitizeUserFacingText() function in errors.js to include the same pattern for consistency.
  • Modify the promptError handler in run.js to handle tool_use/tool_result mismatch errors and return a payload with an actionable message.

Code Changes

// In errors.js - formatAssistantErrorText()
if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(raw)) {
    return ("Conversation history corruption detected. " +
        "Please use /new to start a fresh session.");
}

// In errors.js - sanitizeUserFacingText()
// Add the same pattern for consistency

// In run.js - promptError handler
if (/tool_use_id|tool_result.*corresponding.*tool_use|unexpected.*tool.*block/i.test(errorText)) {
    return {
        payloads: [
            {
                text: "Conversation history corruption detected. " +
                    "Please use /new to start a fresh session.",
                isError: true,
            },
        ],
        meta: {
            durationMs: Date.now() - started,
            agentMeta: {
                sessionId: sessionIdUsed,
                provider,
                model: model.id,
            },
            systemPromptReport: attempt.systemPromptReport,
            error: { kind: "tool_result_mismatch", message: errorText },
        },
    };
}

Verification

To verify that the fix worked, test the following scenarios:

  • Trigger a tool_use/tool_result mismatch error and verify that the user-facing message suggests using /new to start a fresh session.
  • Verify that the infinite loop of error messages is broken and the user can start a new session using /new.

Extra Tips

Consider implementing a more robust fix that attempts automatic transcript repair when this error is detected, such as calling repairToolUseResultPairing() and retrying. This would provide a better user experience and prevent the need for manual intervention.

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 - ✅(Solved) Fix Fix: tool_use_id mismatch error causes infinite loop instead of recovery [2 pull requests, 1 participants]