zed-industries/zed · error

Google AI does not support custom tool calls

Error message

Google AI does not support custom tool calls

What it means

When translating a message history into Google AI (Gemini) request parts, a ToolUse block must carry LanguageModelToolUseInput::Json. Gemini function calling only accepts structured JSON arguments, so a custom (string) tool input cannot be mapped and the conversion bails with this message before a request is sent.

Source

Thrown at crates/google_ai/src/completion.rs:63

                            thought: true,
                            thought_signature: Some(signature),
                        }));
                    }
                }
                MessageContent::Thinking { .. } => {}
                MessageContent::RedactedThinking(_) | MessageContent::Compaction(_) => {}
                MessageContent::Image(image) => {
                    mapped_parts.push(Part::InlineDataPart(InlineDataPart {
                        inline_data: GenerativeContentBlob {
                            mime_type: "image/png".to_string(),
                            data: image.source.to_string(),
                        },
                    }));
                }
                MessageContent::ToolUse(tool_use) => {
                    let thought_signature = tool_use.thought_signature.filter(|s| !s.is_empty());
                    let LanguageModelToolUseInput::Json(input) = tool_use.input else {
                        anyhow::bail!("Google AI does not support custom tool calls");
                    };

                    mapped_parts.push(Part::FunctionCallPart(crate::FunctionCallPart {
                        function_call: crate::FunctionCall {
                            name: tool_use.name.to_string(),
                            args: input,
                            id: Some(tool_use.id.to_string()),
                        },
                        thought_signature,
                    }));
                }
                MessageContent::ToolResult(tool_result) => {
                    let mut text_output = String::new();
                    let mut images: Vec<InlineDataPart> = Vec::new();
                    for part in tool_result.content {
                        match part {
                            language_model_core::LanguageModelToolResultContent::Text(text) => {
                                text_output.push_str(&text);

View on GitHub (pinned to 5a9b9558db)

Solutions

  1. Declare tools with a JSON object input schema so tool calls always produce Json input
  2. Normalize history before mapping: convert custom text input into JSON (e.g. {"input": text}) or drop incompatible turns with a warning
  3. If a tool genuinely takes text, wrap it in a single-field JSON object

Example fix

// before
let LanguageModelToolUseInput::Json(input) = tool_use.input else {
    anyhow::bail!("Google AI does not support custom tool calls");
};

// after: coerce custom text into JSON so history still maps
let input = match tool_use.input {
    LanguageModelToolUseInput::Json(input) => input,
    LanguageModelToolUseInput::Custom(text) => serde_json::json!({ "input": text }),
};
Defensive patterns

Strategy: type-guard

Validate before calling

// before mapping history into a Google AI request
for message in &messages {
    for part in &message.content {
        if let MessageContent::ToolUse(tool_use) = part {
            anyhow::ensure!(
                matches!(tool_use.input, LanguageModelToolUseInput::Json(_)),
                "tool '{}' has non-JSON input; Google AI cannot replay it",
                tool_use.name
            );
        }
    }
}

Type guard

fn is_google_compatible_tool_use(tool_use: &ToolUse) -> bool {
    matches!(tool_use.input, LanguageModelToolUseInput::Json(_))
}

Prevention

When it happens

Trigger: Streaming to Google AI a conversation that contains tool_use blocks created by another provider or code path that used custom/string tool input; any MessageContent::ToolUse whose input is not the Json variant while building the GenerateContent payload.

Common situations: Switching models mid-conversation to Gemini after tools ran with string inputs; replaying cross-provider transcripts through the Google AI backend; older tool definitions whose arguments were raw text.

Related errors


AI-assisted analysis of zed-industries/zed@5a9b9558db (2026-08-20). Data as JSON: /api/errors/51764060d05e835d. Report an issue: GitHub.