Claude Tool Use Not Working Fix
Tool use (function calling) lets Claude call external functions, but misconfigured tool definitions or incorrect response handling will break the interaction loop. This guide covers every common failure.
The Error
When tool definitions are invalid:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "tools.0.input_schema: invalid JSON schema"
}
}
When the response has stop_reason: "tool_use" but your code does not handle it, Claude’s tool call goes unanswered and the conversation stalls.
Quick Fix
- Verify your tool
input_schemais a valid JSON Schema object withtype: "object". - Check
stop_reasonin every response – if it is"tool_use", you must execute the tool and send back atool_result. - When using thinking with tools, set
tool_choicetoauto(notanyor a specific tool).
What Causes This
Tool use fails when:
- The
input_schemais not a valid JSON Schema (missingtype, invalid property definitions). - Your code ignores
stop_reason: "tool_use"and does not send back atool_resultmessage. tool_choiceis set toanyor a specific tool while extended thinking is enabled.strict: trueis used with a schema that has unsupported features (some complex nested schemas cause 500 errors).- The
tool_resultcontent block has the wrongtool_use_id.
Full Solution
Basic Tool Definition
Every tool needs name, description, and a valid input_schema:
import anthropic
import json
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "What's the weather in San Francisco?"}]
)
Handle the Tool Use Loop
When Claude wants to call a tool, the response has stop_reason: "tool_use". You must execute the tool and send back the result:
import anthropic
import json
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "What's the weather in San Francisco?"}]
# Tool use loop -- bounded to 10 iterations max
for iteration in range(10):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages
)
if response.stop_reason == "end_turn":
# Claude is done -- print final text
for block in response.content:
if hasattr(block, "text"):
print(block.text)
break
if response.stop_reason == "tool_use":
# Add assistant response to messages
messages.append({"role": "assistant", "content": response.content})
# Execute each tool call
tool_results = []
for block in response.content:
if block.type == "tool_use":
# Your tool implementation here
result = json.dumps({"temperature": "68F", "conditions": "foggy"})
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
messages.append({"role": "user", "content": tool_results})
Use the SDK Tool Runner (Python)
The Python SDK has a built-in tool runner that handles the loop automatically:
from anthropic import beta_tool
@beta_tool
def get_weather(location: str) -> str:
"""Get the weather for a given location."""
return json.dumps({"location": location, "temperature": "68F"})
runner = client.beta.messages.tool_runner(
max_tokens=1024,
model="claude-sonnet-4-6",
tools=[get_weather],
messages=[{"role": "user", "content": "What's the weather in NYC?"}]
)
Use the SDK Tool Runner (TypeScript with Zod)
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const client = new Anthropic();
const weatherTool = betaZodTool({
name: "get_weather",
inputSchema: z.object({ location: z.string() }),
description: "Get weather for a location",
run: (input) => `Weather in ${input.location} is foggy, 68F`
});
const result = await client.beta.messages.toolRunner({
model: "claude-sonnet-4-6",
max_tokens: 1000,
messages: [{ role: "user", content: "What's the weather in NYC?" }],
tools: [weatherTool]
});
Tool Choice Options
Control whether and how Claude uses tools:
# auto (default): Claude decides whether to use a tool
response = client.messages.create(..., tool_choice={"type": "auto"})
# any: Claude MUST use a tool
response = client.messages.create(..., tool_choice={"type": "any"})
# specific tool: Claude MUST use this exact tool
response = client.messages.create(..., tool_choice={"type": "tool", "name": "get_weather"})
# none: Claude cannot use tools (even if tools are defined)
response = client.messages.create(..., tool_choice={"type": "none"})
Enable Strict Mode
Add strict: true for guaranteed schema conformance on tool inputs:
tools = [
{
"name": "get_weather",
"description": "Get weather",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"],
"additionalProperties": False
}
}
]
Note: With strict: true, your schema must include additionalProperties: false and all properties must be required. Complex nested schemas may cause 500 errors.
Prevention
- Always handle stop_reason “tool_use”: Check every response. If
stop_reasonistool_use, the conversation is incomplete until you send back atool_result. - Use the SDK tool runner: It handles the tool call loop automatically and prevents missed tool calls.
- Validate schemas locally: Test your
input_schemaagainst a JSON Schema validator before sending to the API. - Bound your tool loop: Always set a maximum iteration count to prevent infinite tool call chains.
- Match tool_use_id exactly: The
tool_use_idin yourtool_resultmust match theidfrom thetool_useblock.
Related Guides
- Claude Extended Thinking Not Working – tool_choice restrictions when thinking is enabled.
- Claude API Error 400 invalid_request_error Fix – covers tool schema validation errors.
- Claude Streaming API Guide – stream tool use responses for real-time feedback.
- Claude API Tool Use Function Calling Deep Dive Guide – full parameter reference including tools and tool_choice.
- Claude Python SDK Getting Started – basic SDK setup before implementing tools.