Skip to content

Bug: upstream pi-ai transformMessages creates orphaned toolResults after transcript-sanitize fix (#6118) #6158

@Dinpo

Description

@Dinpo

Summary

Even with the transcript-sanitize extension loaded (fix from #6118), long sessions still hit "unexpected tool_use_id found in tool_result blocks" errors from the Anthropic API. The root cause is in the upstream @mariozechner/pi-ai package's transformMessages() function, which runs after Clawdbot's transcript repair.

Error

Profile anthropic:claude-cli hit Cloud Code Assist format error. Tool calls will be sanitized on retry.
FailoverError: LLM request rejected: messages.268.content.1: unexpected tool_use_id found in tool_result blocks: toolu_01YDDkR7xihJHDSiQdF8VyzV

Environment

Relationship to #6118 and #4650

Issue What it fixes Status
#6118 transcript-sanitize extension not loaded ✅ Patch applied — extension now runs
#4650 limitHistoryTurns creating orphans after repair ✅ Mitigated by #6118 — extension re-repairs on every context build
This issue transformMessages (pi-ai) creates NEW orphans after extension runs ❌ Unfixed — different layer

Root Cause

transformMessages() in @mariozechner/pi-ai (src/providers/transform-messages.ts) strips assistant messages with stopReason === "error" || "aborted" but keeps their toolResult messages. This happens inside the Anthropic provider's convertMessages(), which runs after the transformContext hook where Clawdbot's extension operates:

transformContext hook → repairToolUseResultPairing ✅ (all paired correctly)
    ↓
convertMessages → transformMessages → strips error'd assistant, keeps results ❌
    ↓
convertMessages → builds user message with orphaned tool_result blocks
    ↓
Anthropic API → rejects

Clawdbot cannot fix this from its side because the orphans are created inside the pi-ai provider after all hooks have run.

Additional note: misleading retry log

The log message "Tool calls will be sanitized on retry" is misleading — the retry path rotates auth profiles but does not add any additional sanitization. The same corrupt transcript is sent again, hitting the same error.

Workaround

Patch node_modules/@mariozechner/pi-ai/dist/providers/transform-messages.js directly (overwritten on update):

+    const skippedToolCallIds = new Set();
     for (let i = 0; i < transformed.length; i++) {
         const msg = transformed[i];
         if (msg.role === "assistant") {
             const assistantMsg = msg;
             if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
+                for (const block of assistantMsg.content) {
+                    if (block.type === "toolCall" && block.id) {
+                        skippedToolCallIds.add(block.id);
+                    }
+                }
                 continue;
             }
         }
         else if (msg.role === "toolResult") {
+            if (skippedToolCallIds.has(msg.toolCallId)) {
+                continue;
+            }
             existingToolResultIds.add(msg.toolCallId);
             result.push(msg);
         }

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions