zed-industries/zed · error
Anthropic does not support custom tool calls
Error message
Anthropic does not support custom tool calls
What it means
When converting a conversation into Anthropic Messages API requests, tool_use content must carry structured JSON input (Anthropic requires an 'input' object). LanguageModelToolUseInput::Text — free-form text produced by custom/untyped tools — has no faithful Anthropic encoding, so the conversion refuses it rather than sending a malformed request.
Source
Thrown at crates/anthropic/src/completion.rs:171
Ok(None)
}
}
MessageContent::Image(image) => Ok(Some(RequestContent::Image {
source: ImageSource {
source_type: "base64".to_string(),
media_type: "image/png".to_string(),
data: image.source.to_string(),
},
cache_control: None,
})),
MessageContent::ToolUse(tool_use) => match tool_use.input {
LanguageModelToolUseInput::Json(input) => Ok(Some(RequestContent::ToolUse {
id: tool_use.id.to_string(),
name: tool_use.name.to_string(),
input,
cache_control: None,
})),
LanguageModelToolUseInput::Text(_) => Err(anyhow::anyhow!(
"Anthropic does not support custom tool calls"
)),
},
MessageContent::ToolResult(tool_result) => {
let content = match tool_result.content.as_slice() {
[LanguageModelToolResultContent::Text(text)] => {
ToolResultContent::Plain(text.to_string())
}
_ => {
let parts = tool_result
.content
.into_iter()
.map(|part| match part {
LanguageModelToolResultContent::Text(text) => ToolResultPart::Text {
text: text.to_string(),
},
LanguageModelToolResultContent::Image(image) => ToolResultPart::Image {
source: ImageSource {View on GitHub (pinned to bc538def45)
Solutions
- Use tools whose inputs are JSON (structured input schema) when Anthropic is the backend.
- Start a new thread after switching to Anthropic instead of replaying old tool calls.
- As a developer: convert Text input before serialization (e.g. wrap it as a JSON object) or skip incompatible turns with a warning.
Example fix
// before
LanguageModelToolUseInput::Text(_) => Err(anyhow::anyhow!(
"Anthropic does not support custom tool calls"
)),
// after: wrap free-form text as JSON input
LanguageModelToolUseInput::Text(text) => Ok(Some(RequestContent::ToolUse {
id: tool_use.id.to_string(),
name: tool_use.name.to_string(),
input: serde_json::json!({ "text": text.to_string() }),
cache_control: None,
})), Defensive patterns
Strategy: type-guard
Validate before calling
for message in &request.messages {
for content in &message.content {
if let MessageContent::ToolUse(tool_use) = content {
if matches!(tool_use.input, LanguageModelToolUseInput::Text(_)) {
// convert to JSON or reject before handing the request to Anthropic
}
}
}
} Type guard
fn is_anthropic_compatible(request: &LanguageModelRequest) -> bool {
request
.messages
.iter()
.flat_map(|message| message.content.iter())
.all(|content| match content {
MessageContent::ToolUse(tool_use) => {
matches!(tool_use.input, LanguageModelToolUseInput::Json(_))
}
_ => true,
})
} Prevention
- Define tool input schemas as JSON objects rather than free-form text.
- Gate provider switching on compatibility, or start a fresh thread when replaying is impossible.
- Convert Text tool inputs at the boundary where custom tools are invoked.
When it happens
Trigger: Replaying a thread that contains a tool call with text input into the Anthropic backend; switching models/providers mid-thread; custom tools that attach unstructured text as tool input.
Common situations: Switching the agent from another provider to Anthropic on an existing thread; custom tools without JSON schemas; imported transcripts or fixtures containing Text tool uses.
Related errors
- Claude returned no tool_use block for tool '{tool['name']}'
- Sentry API returned HTTP {error.code} for {path}: {detail}
- Sentry API returned HTTP {err.code} for {path}: {detail}
- unsupported Anthropic compaction state format: {}
- Anthropic does not support custom tools
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/d5c584e59bb35c9a.
Report an issue: GitHub.