Tool use id bug #3647

Open
opened 2026-02-16 17:40:59 -05:00 by yindo · 9 comments
Owner

Originally created by @leoisadev1 on GitHub (Dec 18, 2025).

Originally assigned to: @rekram1-node on GitHub.

Description

I uploaded 2 images and a short message, got hit with this error.

Image

OpenCode version

1.0.167

Steps to reproduce

  1. Upload 2 images with ctrl + v
  2. Add a short message
  3. Click enter

Screenshot and/or share link

Image

Operating System

macOS 26.2

Terminal

Ghostty

Originally created by @leoisadev1 on GitHub (Dec 18, 2025). Originally assigned to: @rekram1-node on GitHub. ### Description I uploaded 2 images and a short message, got hit with this error. <img width="1596" height="78" alt="Image" src="https://github.com/user-attachments/assets/3d9d2d6a-42c7-4eb0-8efe-3afd73dd50c1" /> ### OpenCode version 1.0.167 ### Steps to reproduce 1. Upload 2 images with ctrl + v 2. Add a short message 3. Click enter ### Screenshot and/or share link <img width="1596" height="78" alt="Image" src="https://github.com/user-attachments/assets/3bb2322c-3ecf-4550-b3f6-06bebdd108b4" /> ### Operating System macOS 26.2 ### Terminal Ghostty
yindo added the bug label 2026-02-16 17:40:59 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Dec 18, 2025):

This issue might be a duplicate of existing issues. Please check:

  • #2720: AI_APICallError: tool_use blocks found without corresponding tool_result blocks
  • #1662: AI_APICallError: tool_use ids were found without tool_result blocks immediately after
  • #1279: AI_APICallError: messages.1.content.2: tool_use ids must be unique
  • #1150: AI_APICallError: tool_use ids must be unique
  • #4077: In Memory Image can't be Pasted in Opencode After TUI Upgrade (image-related issues)
  • #5101: Switching from an image supported model to a non-image model breaks the thread (image handling)

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Dec 18, 2025): This issue might be a duplicate of existing issues. Please check: - #2720: AI_APICallError: tool_use blocks found without corresponding tool_result blocks - #1662: AI_APICallError: `tool_use` ids were found without `tool_result` blocks immediately after - #1279: AI_APICallError: messages.1.content.2: `tool_use` ids must be unique - #1150: AI_APICallError: `tool_use` ids must be unique - #4077: In Memory Image can't be Pasted in Opencode After TUI Upgrade (image-related issues) - #5101: Switching from an image supported model to a non-image model breaks the thread (image handling) Feel free to ignore if none of these address your specific case.
Author
Owner

@rekram1-node commented on GitHub (Dec 18, 2025):

ill look into it

@rekram1-node commented on GitHub (Dec 18, 2025): ill look into it
Author
Owner

@rekram1-node commented on GitHub (Dec 18, 2025):

Image

How do I replicate?

I saw ur description but I think this prolly has to do w/ ordering and whatnot

If u could export session: opencode export > session.json it'd be amazing

@rekram1-node commented on GitHub (Dec 18, 2025): <img width="1016" height="576" alt="Image" src="https://github.com/user-attachments/assets/7cfdd714-8654-405b-838c-5a91292dbdc2" /> How do I replicate? I saw ur description but I think this prolly has to do w/ ordering and whatnot If u could export session: `opencode export > session.json` it'd be amazing
Author
Owner

@deriegle commented on GitHub (Dec 19, 2025):

https://gist.github.com/deriegle/db117ea51f6f55e814e855541bcd47ee

I ran into this too. I had a longer session and was on the main branch in my repo. I had a few files staged and ran /rmslop (same as in opencode) and ran into this bug. I opened a new session and ran /rmslop and got the error again. The gist above is from the second session.

Image

EDIT: realizing the way mine happened was different (no images uploaded) but the error was similar in case that's helpful. Otherwise we can mark this as unrelated.

@deriegle commented on GitHub (Dec 19, 2025): https://gist.github.com/deriegle/db117ea51f6f55e814e855541bcd47ee I ran into this too. I had a longer session and was on the `main` branch in my repo. I had a few files staged and ran `/rmslop` (same as in opencode) and ran into this bug. I opened a new session and ran `/rmslop` and got the error again. The gist above is from the second session. <img width="1043" height="691" alt="Image" src="https://github.com/user-attachments/assets/b21ee454-77c9-434f-9ca1-d0783194bc02" /> EDIT: realizing the way mine happened was different (no images uploaded) but the error was similar in case that's helpful. Otherwise we can mark this as unrelated.
Author
Owner

@rekram1-node commented on GitHub (Dec 19, 2025):

@deriegle can u give me the raw json export tho? opencode export > session.json

@rekram1-node commented on GitHub (Dec 19, 2025): @deriegle can u give me the raw json export tho? `opencode export > session.json`
Author
Owner

@deriegle commented on GitHub (Dec 19, 2025):

Sorry about that. I've added the session.json into this gist: https://gist.github.com/deriegle/db117ea51f6f55e814e855541bcd47ee

Several of the calls outputted my entire files since it was diffing them, I obfuscated the file names & outputs. If those end up being required to debug this, I can find another way to share that.

@deriegle commented on GitHub (Dec 19, 2025): Sorry about that. I've added the session.json into this gist: https://gist.github.com/deriegle/db117ea51f6f55e814e855541bcd47ee Several of the calls outputted my entire files since it was diffing them, I obfuscated the file names & outputs. If those end up being required to debug this, I can find another way to share that.
Author
Owner

@Mishkun commented on GitHub (Dec 23, 2025):

https://gist.github.com/Mishkun/02648359700010e559f7edf6bc8ac4dd

@rekram1-node I finally got this on a pet project, so I can share one in the full

@Mishkun commented on GitHub (Dec 23, 2025): https://gist.github.com/Mishkun/02648359700010e559f7edf6bc8ac4dd @rekram1-node I finally got this on a pet project, so I can share one in the full
Author
Owner

@westito commented on GitHub (Jan 8, 2026):

I get this error many times so logged out and tried investigate with Opus. What I found.
(I posted this on other issue, but marked as off-topic - this issue is more related)

Tool Validation Error: Missing tool_result After Interruption

Root Cause

Client doesn't insert tool_result message after interruption before allowing model to continue.

When abort happens, client appends retry to the SAME assistant message instead of starting a new turn with tool_result in between.

Summary

Anthropic's Messages API rejects conversation history when an assistant message contains content (thinking/text) AFTER a tool_use block. The API expects tool_result immediately after tool_use, but when content appears in between, validation fails.

Error Message

messages.{N}: `tool_use` ids were found without `tool_result` blocks immediately after: {tool_id}. 
Each `tool_use` block must have a corresponding `tool_result` block in the next message.

The Rule

All tool_use blocks must be contiguous at the END of the assistant message content.

Once a tool_use block appears, only more tool_use blocks can follow. Any other content (thinking, text) AFTER a tool_use is invalid.

Valid vs Invalid Patterns

Valid: Tools grouped at end

assistant: [thinking, text, tool_use_A, tool_use_B]
user: [tool_result_A, tool_result_B]

Valid: Separate turns per tool

assistant: [thinking, text, tool_use_A]
user: [tool_result_A]
assistant: [thinking, text, tool_use_B]
user: [tool_result_B]

Invalid: Content AFTER tool_use

assistant: [thinking, tool_use_A, thinking, tool_use_B]  ← thinking AFTER tool_A!
user: [tool_result_A, tool_result_B]

Error: tool_use ids were found without tool_result blocks immediately after: tool_A

The API sees content after tool_use_A and interprets it as: "tool_A should have gotten its result before the assistant generated more content."

How This Happens

When a user aborts a tool mid-stream:

What should happen:

assistant: [thinking, tool_A]       ← END turn here
user: [tool_result_A: "aborted"]    ← INSERT this
assistant: [thinking, tool_B]       ← NEW turn
user: [tool_result_B]

What actually happens:

assistant: [thinking, tool_A, thinking, tool_B]  ← appended to SAME message
user: [tool_result_A, tool_result_B]             ← both results bundled

The sequence:

  1. Model generates: thinking → tool_A
  2. User aborts during tool execution
  3. Client saves: thinking → tool_A (aborted)
  4. BUG: Client does NOT end turn and add tool_result
  5. Model retries, client appends: thinking → tool_B
  6. Result: [thinking, tool_A, thinking, tool_B] - invalid pattern

Other causes:

  • Network interruption during streaming
  • Timeout kills the stream
  • Client crash during streaming
  • Rate limiting mid-response

Real-World Example

Saved message parts

prt_..5001 → step-start
prt_..7001 → reasoning: "The user wants me to create a markdown file..."
prt_..6001 → tool: write (ERROR, input: {})  ← ABORTED
prt_..b001 → reasoning: "The user wants me to create a markdown file..."
prt_..7001 → tool: write (COMPLETED)
prt_..c001 → step-finish

Converted to API format:

{
  "role": "assistant",
  "content": [
    {"type": "thinking", "thinking": "..."},
    {"type": "tool_use", "id": "toolu_01LiPBp", "name": "write", "input": {}},
    {"type": "thinking", "thinking": "..."},
    {"type": "tool_use", "id": "toolu_016rCwe", "name": "write", "input": {...}}
  ]
}

API Error:

messages.49: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01LiPBp

Workaround

Before sending to API, restructure messages with interleaved content:

Option A - Split into separate turns:

Original: assistant[thinking, tool_A, thinking, tool_B] + user[result_A, result_B]

Fixed:
  assistant[thinking, tool_A] + user[result_A]
  assistant[thinking, tool_B] + user[result_B]

Option B - Group tools at end (if results exist):

Original: [thinking, tool_A, thinking, tool_B]

Fixed: [thinking, thinking, tool_A, tool_B]

Note: Only works if the tool results are bundled together in the next message.

Test Results

Pattern Result
[thinking, text, tool_A, tool_B][result_A, result_B] SUCCESS
[thinking, tool_A, thinking, tool_B][result_A, result_B] FAILED
[thinking, text, tool_A, thinking, text, tool_B][result_A, result_B] FAILED
[thinking, text, tool_A][result_A][thinking, text, tool_B][result_B] SUCCESS

Minimal Reproduction

curl -X POST https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: interleaved-thinking-2025-05-14" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-20250514",
    "max_tokens": 2048,
    "thinking": {"type": "enabled", "budget_tokens": 1024},
    "messages": [
      {"role": "user", "content": "Do two things"},
      {
        "role": "assistant",
        "content": [
          {"type": "thinking", "thinking": "First.", "signature": "VALID_SIG_HERE"},
          {"type": "tool_use", "id": "tool_1", "name": "do_thing", "input": {"x": 1}},
          {"type": "thinking", "thinking": "Second.", "signature": "VALID_SIG_HERE"},
          {"type": "tool_use", "id": "tool_2", "name": "do_thing", "input": {"x": 2}}
        ]
      },
      {
        "role": "user",
        "content": [
          {"type": "tool_result", "tool_use_id": "tool_1", "content": "done"},
          {"type": "tool_result", "tool_use_id": "tool_2", "content": "done"}
        ]
      }
    ],
    "tools": [{"name": "do_thing", "description": "Do", "input_schema": {"type": "object", "properties": {"x": {"type": "integer"}}}}]
  }'

Expected error:

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "messages.1: `tool_use` ids were found without `tool_result` blocks immediately after: tool_1"
  }
}
@westito commented on GitHub (Jan 8, 2026): I get this error many times so logged out and tried investigate with Opus. What I found. (I posted this on other issue, but marked as off-topic - this issue is more related) # Tool Validation Error: Missing tool_result After Interruption ## Root Cause **Client doesn't insert `tool_result` message after interruption before allowing model to continue.** When abort happens, client appends retry to the SAME assistant message instead of starting a new turn with tool_result in between. ## Summary Anthropic's Messages API rejects conversation history when an assistant message contains **content (thinking/text) AFTER a tool_use block**. The API expects tool_result immediately after tool_use, but when content appears in between, validation fails. ## Error Message ``` messages.{N}: `tool_use` ids were found without `tool_result` blocks immediately after: {tool_id}. Each `tool_use` block must have a corresponding `tool_result` block in the next message. ``` ## The Rule **All `tool_use` blocks must be contiguous at the END of the assistant message content.** Once a `tool_use` block appears, only more `tool_use` blocks can follow. Any other content (thinking, text) AFTER a `tool_use` is invalid. ## Valid vs Invalid Patterns ### Valid: Tools grouped at end ``` assistant: [thinking, text, tool_use_A, tool_use_B] user: [tool_result_A, tool_result_B] ``` ### Valid: Separate turns per tool ``` assistant: [thinking, text, tool_use_A] user: [tool_result_A] assistant: [thinking, text, tool_use_B] user: [tool_result_B] ``` ### Invalid: Content AFTER tool_use ``` assistant: [thinking, tool_use_A, thinking, tool_use_B] ← thinking AFTER tool_A! user: [tool_result_A, tool_result_B] ``` **Error:** `tool_use ids were found without tool_result blocks immediately after: tool_A` The API sees content after `tool_use_A` and interprets it as: "tool_A should have gotten its result before the assistant generated more content." ## How This Happens When a user **aborts** a tool mid-stream: **What should happen:** ``` assistant: [thinking, tool_A] ← END turn here user: [tool_result_A: "aborted"] ← INSERT this assistant: [thinking, tool_B] ← NEW turn user: [tool_result_B] ``` **What actually happens:** ``` assistant: [thinking, tool_A, thinking, tool_B] ← appended to SAME message user: [tool_result_A, tool_result_B] ← both results bundled ``` **The sequence:** 1. Model generates: `thinking → tool_A` 2. User aborts during tool execution 3. Client saves: `thinking → tool_A (aborted)` 4. **BUG:** Client does NOT end turn and add tool_result 5. Model retries, client appends: `thinking → tool_B` 6. Result: `[thinking, tool_A, thinking, tool_B]` - invalid pattern ### Other causes: - Network interruption during streaming - Timeout kills the stream - Client crash during streaming - Rate limiting mid-response ## Real-World Example **Saved message parts** ``` prt_..5001 → step-start prt_..7001 → reasoning: "The user wants me to create a markdown file..." prt_..6001 → tool: write (ERROR, input: {}) ← ABORTED prt_..b001 → reasoning: "The user wants me to create a markdown file..." prt_..7001 → tool: write (COMPLETED) prt_..c001 → step-finish ``` **Converted to API format:** ```json { "role": "assistant", "content": [ {"type": "thinking", "thinking": "..."}, {"type": "tool_use", "id": "toolu_01LiPBp", "name": "write", "input": {}}, {"type": "thinking", "thinking": "..."}, {"type": "tool_use", "id": "toolu_016rCwe", "name": "write", "input": {...}} ] } ``` **API Error:** ``` messages.49: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01LiPBp ``` ## Workaround Before sending to API, restructure messages with interleaved content: **Option A - Split into separate turns:** ``` Original: assistant[thinking, tool_A, thinking, tool_B] + user[result_A, result_B] Fixed: assistant[thinking, tool_A] + user[result_A] assistant[thinking, tool_B] + user[result_B] ``` **Option B - Group tools at end (if results exist):** ``` Original: [thinking, tool_A, thinking, tool_B] Fixed: [thinking, thinking, tool_A, tool_B] ``` Note: Only works if the tool results are bundled together in the next message. ## Test Results | Pattern | Result | |---------|--------| | `[thinking, text, tool_A, tool_B]` → `[result_A, result_B]` | ✅ SUCCESS | | `[thinking, tool_A, thinking, tool_B]` → `[result_A, result_B]` | ❌ FAILED | | `[thinking, text, tool_A, thinking, text, tool_B]` → `[result_A, result_B]` | ❌ FAILED | | `[thinking, text, tool_A]` → `[result_A]` → `[thinking, text, tool_B]` → `[result_B]` | ✅ SUCCESS | ## Minimal Reproduction ```bash curl -X POST https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: interleaved-thinking-2025-05-14" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 2048, "thinking": {"type": "enabled", "budget_tokens": 1024}, "messages": [ {"role": "user", "content": "Do two things"}, { "role": "assistant", "content": [ {"type": "thinking", "thinking": "First.", "signature": "VALID_SIG_HERE"}, {"type": "tool_use", "id": "tool_1", "name": "do_thing", "input": {"x": 1}}, {"type": "thinking", "thinking": "Second.", "signature": "VALID_SIG_HERE"}, {"type": "tool_use", "id": "tool_2", "name": "do_thing", "input": {"x": 2}} ] }, { "role": "user", "content": [ {"type": "tool_result", "tool_use_id": "tool_1", "content": "done"}, {"type": "tool_result", "tool_use_id": "tool_2", "content": "done"} ] } ], "tools": [{"name": "do_thing", "description": "Do", "input_schema": {"type": "object", "properties": {"x": {"type": "integer"}}}}] }' ``` **Expected error:** ```json { "type": "error", "error": { "type": "invalid_request_error", "message": "messages.1: `tool_use` ids were found without `tool_result` blocks immediately after: tool_1" } } ```
Author
Owner

@prolific commented on GitHub (Feb 12, 2026):

I am also getting this error way too many times. Makes the current session completely unusable.

@prolific commented on GitHub (Feb 12, 2026): I am also getting this error way too many times. Makes the current session completely unusable.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#3647