Skip to content

fix: sanitize pending/running tool parts to prevent orphaned tool_use#54

Merged
BYK merged 1 commit intomainfrom
fix/sanitize-orphaned-tool-use
Mar 25, 2026
Merged

fix: sanitize pending/running tool parts to prevent orphaned tool_use#54
BYK merged 1 commit intomainfrom
fix/sanitize-orphaned-tool-use

Conversation

@BYK
Copy link
Copy Markdown
Owner

@BYK BYK commented Mar 25, 2026

Problem

When a session errors mid-tool-execution (e.g. context overflow), the tool part stays in pending/running state. On the next transform(), the OpenCode SDK generates a tool_use block for this part but has no output to generate a matching tool_result, causing Anthropic to reject with:

messages.685: tool_use ids were found without tool_result blocks immediately after: toolu_017XWb5fnou7kqTt3CjSfiwc

Root Cause

In the SDK's ToolState discriminated union:

  • ToolStatePending / ToolStateRunning → only generates tool_use (no output → no tool_result)
  • ToolStateCompleted / ToolStateError → generates both tool_use + tool_result

Lore's gradient transform had no sanitization step for incomplete tool parts. The trailing drop loop preserved messages with ANY tool part regardless of status, which was correct for preventing the prefill error but didn't address the orphaned tool_use issue.

Fix

Add sanitizeToolParts() in gradient.ts that converts pending/running tool parts to error state after transformInner(). This:

  • Generates both tool_use + tool_result(is_error=true), eliminating the orphan
  • Preserves conversation history — the model sees the tool was attempted but failed
  • Is a no-op (returns same array reference) when all tools are in terminal state (common case)
  • Runs at all layers (0-4), covering every code path

Changes

  • src/gradient.ts: Add sanitizeToolParts() function and integrate into transform()
  • src/index.ts: Update trailing drop loop comment to note sanitization happens upstream
  • test/gradient.test.ts: 6 new test cases covering pending/running/mixed/no-op scenarios

@BYK BYK enabled auto-merge (squash) March 25, 2026 21:15
When a session errors mid-tool-execution (e.g. context overflow), the tool
part stays in pending/running state. On the next transform, the SDK generates
a tool_use block for this part but has no output to generate a matching
tool_result, causing Anthropic to reject with:

  tool_use ids were found without tool_result blocks immediately after

Add sanitizeToolParts() in gradient.ts that converts pending/running tool
parts to error state after transformInner(). Error state generates both
tool_use + tool_result(is_error=true), eliminating the orphan while
preserving conversation history. The function is a no-op (returns same
array reference) when all tools are already in terminal state.
@BYK BYK force-pushed the fix/sanitize-orphaned-tool-use branch from 62a44f9 to b3e149a Compare March 25, 2026 21:30
@BYK BYK merged commit 59f11a6 into main Mar 25, 2026
1 check passed
@BYK BYK deleted the fix/sanitize-orphaned-tool-use branch March 25, 2026 21:31
@craft-deployer craft-deployer bot mentioned this pull request Mar 25, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant