{"record":{"id":"51764060d05e835d","repo":"zed-industries/zed","slug":"google-ai-does-not-support-custom-tool-calls","errorCode":null,"errorMessage":"Google AI does not support custom tool calls","messagePattern":"Google AI does not support custom tool calls","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/google_ai/src/completion.rs","lineNumber":63,"sourceCode":"                            thought: true,\n                            thought_signature: Some(signature),\n                        }));\n                    }\n                }\n                MessageContent::Thinking { .. } => {}\n                MessageContent::RedactedThinking(_) | MessageContent::Compaction(_) => {}\n                MessageContent::Image(image) => {\n                    mapped_parts.push(Part::InlineDataPart(InlineDataPart {\n                        inline_data: GenerativeContentBlob {\n                            mime_type: \"image/png\".to_string(),\n                            data: image.source.to_string(),\n                        },\n                    }));\n                }\n                MessageContent::ToolUse(tool_use) => {\n                    let thought_signature = tool_use.thought_signature.filter(|s| !s.is_empty());\n                    let LanguageModelToolUseInput::Json(input) = tool_use.input else {\n                        anyhow::bail!(\"Google AI does not support custom tool calls\");\n                    };\n\n                    mapped_parts.push(Part::FunctionCallPart(crate::FunctionCallPart {\n                        function_call: crate::FunctionCall {\n                            name: tool_use.name.to_string(),\n                            args: input,\n                            id: Some(tool_use.id.to_string()),\n                        },\n                        thought_signature,\n                    }));\n                }\n                MessageContent::ToolResult(tool_result) => {\n                    let mut text_output = String::new();\n                    let mut images: Vec<InlineDataPart> = Vec::new();\n                    for part in tool_result.content {\n                        match part {\n                            language_model_core::LanguageModelToolResultContent::Text(text) => {\n                                text_output.push_str(&text);","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/zed-industries/zed/blob/5a9b9558db01a6b906cec2fb70a797affdc58cdd/crates/google_ai/src/completion.rs#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Declare tools with a JSON object input schema so tool calls always produce Json input","Normalize history before mapping: convert custom text input into JSON (e.g. {\"input\": text}) or drop incompatible turns with a warning","If a tool genuinely takes text, wrap it in a single-field JSON object"],"exampleFix":"// before\nlet LanguageModelToolUseInput::Json(input) = tool_use.input else {\n    anyhow::bail!(\"Google AI does not support custom tool calls\");\n};\n\n// after: coerce custom text into JSON so history still maps\nlet input = match tool_use.input {\n    LanguageModelToolUseInput::Json(input) => input,\n    LanguageModelToolUseInput::Custom(text) => serde_json::json!({ \"input\": text }),\n};","handlingStrategy":"type-guard","validationCode":"// before mapping history into a Google AI request\nfor message in &messages {\n    for part in &message.content {\n        if let MessageContent::ToolUse(tool_use) = part {\n            anyhow::ensure!(\n                matches!(tool_use.input, LanguageModelToolUseInput::Json(_)),\n                \"tool '{}' has non-JSON input; Google AI cannot replay it\",\n                tool_use.name\n            );\n        }\n    }\n}","typeGuard":"fn is_google_compatible_tool_use(tool_use: &ToolUse) -> bool {\n    matches!(tool_use.input, LanguageModelToolUseInput::Json(_))\n}","tryCatchPattern":null,"preventionTips":["Define tool input schemas as JSON objects from the start","Normalize cross-provider transcripts: convert or strip custom tool inputs before dispatch","When conversion fails, name the offending tool id in the error to speed debugging"],"tags":["google-ai","gemini","tool-calling","llm-provider","message-mapping"],"backgroundTag":"unsupported-tool-input-format","analyzedSha":"5a9b9558db01a6b906cec2fb70a797affdc58cdd","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}