CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude.ai
Claude.ai/2026-05-04Intermediate

How to Fix the "Tool Result Could Not Be Submitted" Error in Claude

A practical guide to diagnosing and fixing the "tool result could not be submitted" error in Claude's tool use API, based on real development experience.

tool-use22error-fix4API27troubleshooting87tool-result

If you're building with Claude's tool use API, you've probably run into the "tool result could not be submitted" error at some point. It's one of those frustrating errors where the message alone doesn't tell you much about what actually went wrong.

I've hit this error more times than I'd like to admit while implementing tool use in my own projects. After working through the patterns, here's a practical breakdown of the causes and fixes.

When Does This Error Appear?

"Tool result could not be submitted" appears during the tool use flow, which works like this:

  1. A user sends a message
  2. Claude decides to call a tool and returns a response containing a tool_use block
  3. Your code executes the tool and retrieves a result
  4. You send that result back as a tool_result in the next message

When step four fails, you get this error. The message might suggest the problem is on Claude's end, but in most cases it's an implementation issue you can fix.

Common Causes and Fixes

1. Tool Result Is Too Large (Most Common)

This is the most frequent culprit. If you pass raw HTML from a web scraper, a large database dump, or any unfiltered external data as your tool result, you can easily exceed Claude's context window.

Most Claude models support up to roughly 200,000 tokens of context. When the total conversation — including tool results — approaches that limit, the API can't accept new submissions.

The fix: Pre-process your tool results and return only the information Claude actually needs.

# ❌ Returning raw HTML (dangerous)
def search_web(query: str) -> str:
    response = requests.get(f"https://example.com/search?q={query}")
    return response.text  # Could be tens of thousands of characters
 
# ✅ Extracting only what matters (correct)
def search_web(query: str) -> str:
    response = requests.get(f"https://example.com/search?q={query}")
    soup = BeautifulSoup(response.text, 'html.parser')
    
    results = []
    for item in soup.select('.result')[:5]:  # Top 5 results only
        title = item.select_one('h3')
        snippet = item.select_one('.snippet')
        if title and snippet:
            results.append(f"Title: {title.text}\nSummary: {snippet.text[:200]}")
    
    return "\n\n".join(results) if results else "No results found"

A good rule of thumb: keep each individual tool result under 5,000–10,000 tokens.

2. Mismatched tool_use_id

Claude's tool_use block includes an id field. Your tool_result must reference that exact ID in tool_use_id. Any mismatch will cause the submission to fail.

response = anthropic.messages.create(...)
 
# ❌ Hardcoding or guessing the ID
tool_results = [{
    "type": "tool_result",
    "tool_use_id": "toolu_01",  # This will fail unless it matches exactly
    "content": result_content
}]
 
# ✅ Using the ID from Claude's actual response
for block in response.content:
    if block.type == "tool_use":
        tool_result = {
            "type": "tool_result",
            "tool_use_id": block.id,  # ← Use the ID Claude gave you
            "content": str(tool_output)
        }

This becomes especially tricky when Claude calls multiple tools in parallel — make sure you're pairing each result with the correct ID.

3. Wrong content Type

The content field in a tool_result must be a string or an array (for multi-modal content including images). Passing None, integers, or other types will cause issues.

# ❌ Passing non-string types
"content": None
"content": 42
 
# ✅ Always convert to string
"content": str(result) if result is not None else "No result returned from tool"

When a tool execution fails, use is_error: true instead of an empty string:

{
    "type": "tool_result",
    "tool_use_id": block.id,
    "content": "Error: Database connection timed out after 30 seconds",
    "is_error": True  # Claude will recognize this and try to recover
}

4. Network or Timeout Issues

Long-running tools can cause the underlying HTTP connection to time out before you can submit the result. This is common with tools that call external APIs or process large files.

import anthropic
import httpx
 
# Set a longer timeout explicitly
client = anthropic.Anthropic(
    http_client=httpx.Client(timeout=60.0)  # 60 seconds
)

For tools that genuinely take a long time, consider breaking the work into smaller chunks or using asynchronous processing.

5. Malformed Message History

As conversations grow, it's easy for the messages array structure to become inconsistent. Tool use has strict requirements about message ordering and format.

# Correct message structure for tool use
messages = [
    # 1. User message
    {"role": "user", "content": "What's the weather in Tokyo?"},
    
    # 2. Claude's tool call response
    {"role": "assistant", "content": [
        {"type": "text", "text": "Let me check the weather for you."},
        {"type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {"city": "Tokyo"}}
    ]},
    
    # 3. Tool result (sent as "user" role)
    {"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": "toolu_01", "content": "Tokyo: Sunny, 25°C"}
    ]},
    
    # 4. Continue the conversation...
]

Every assistant message that includes a tool_use block must be followed by a user message with the corresponding tool_result before you can continue the conversation.

Debugging Checklist

When you hit this error, work through these checks in order:

Verify the tool_use_id match first — print both the ID from Claude's response and the ID in your tool_result and confirm they're identical. Then check the result size by printing len(result_content) to see if you're returning unexpectedly large data. After that, check the content type with type(result_content) and ensure it's a str. Finally, inspect the message array structure to confirm roles alternate correctly and every tool call has a corresponding result.

A Note on Streaming

If you're using the streaming API, the same root causes apply, but tracking them is harder because the response comes in chunks. I'd recommend debugging in non-streaming mode first to confirm the tool use logic is correct, then switching to streaming once everything works.

Wrapping Up

The "tool result could not be submitted" error almost always comes down to one of these five patterns. Oversized results are the most surprising one — code that works fine in development can suddenly fail in production when it encounters real-world data volumes.

Adding explicit size limits to your tool implementations early in development will save you significant debugging time later.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude.ai2026-04-23
When Claude Says 'Failed to Send Message' — A Practical Triage and Recovery Guide
Why Claude shows 'Failed to send message' — broken down into five real causes, with a quick triage order and practical fixes for each.
Claude.ai2026-04-09
Fixing Claude Extended Thinking When It Stops, Times Out, or Loops
Extended Thinking stopping mid-process, hitting timeouts, or consuming unexpected costs? This guide covers root causes, correct budget_tokens configuration, streaming patterns, retry handling, and cost optimization strategies.
Claude.ai2026-04-09
Complete Fix Guide: Claude File Attachment and Image Recognition Failures
Solve issues where PDFs, images, or text files attached to Claude aren't loading or throw errors. This guide covers file format support, size limits, and browser settings—everything you need to fix file upload problems.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →