Where to Look When the Model Stays Silent
You define a schema, send the request, and the model replies with plain prose. No function_call in the response, no error to chase. Function Calling failures often begin exactly here — with nothing happening at all.
The causes sit in different layers: how the schema is written, which model you picked, how the response gets parsed. Error messages rarely point at the right one. The sections below start from five symptoms you are likely to hit and work back to the cause behind each, with code you can run directly.
How Function Calling Works (Quick Recap)
Before diving into fixes, let's confirm the expected flow:
- Your app sends a tool definition (schema) + user message to the API
- The model decides to call a tool and returns a
function_callin the response - Your app executes the tool and sends the result back as a
function_response - The model uses the result to generate a final answer
Most issues live in steps 1, 2, or 3. Let's work through them by symptom.
Problem 1: The Tool Is Never Called
Why This Happens
If the model ignores your tool entirely, the most likely causes are:
- Vague tool description: The model reads your
descriptionfield to decide when to use a tool. Ambiguous descriptions lead to tools being skipped - Poor alignment between the question and the tool: The user's message and the tool's purpose don't clearly match
- Using an incompatible model: Not all Gemini variants support Function Calling equally
- Missing
tool_choiceoverride: You can force tool usage during debugging
How to Fix It
Start by improving your tool's description. Here's a comparison:
import google.generativeai as genai
genai.configure(api_key="YOUR_GEMINI_API_KEY")
# ❌ Bad: Too vague — the model can't determine when to use this
bad_tool = {
"name": "get_weather",
"description": "Gets weather.",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
# ✅ Good: Clear trigger condition and concrete examples
good_tool = {
"name": "get_weather",
"description": (
"Retrieves current weather conditions for a specified city or region. "
"Use this when the user asks about weather, temperature, rainfall, wind speed, "
"or any other atmospheric conditions. "
"Examples: 'What's the weather in Tokyo?', 'Will it rain in London today?'"
),
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or region (e.g., Tokyo, London, New York)"
}
},
"required": ["location"]
}
}
# Use mode: ANY to force tool usage — great for debugging
model = genai.GenerativeModel(
model_name="gemini-2.5-pro",
tools=[good_tool],
tool_config={"function_calling_config": {"mode": "ANY"}}
)
response = model.generate_content("What's the weather like in Tokyo right now?")
print(response.candidates[0].content.parts)
# Expected output: [function_call { name: "get_weather", args: { location: "Tokyo" } }]The three mode options for tool_config:
AUTO(default): Model decides whether to use toolsANY: Model must call one of the provided toolsNONE: Model will not use any tools
Problem 2: Schema Validation Errors (400 Bad Request)
Why This Happens
When you see 400 Bad Request or Invalid argument, your tool schema is likely malformed. Common mistakes include:
- Typos in
requiredfield names that don't matchpropertieskeys - Unsupported type names (e.g.,
"int"instead of"integer") - Missing type definitions in nested objects
- Putting
enumvalues in the wrong place
How to Fix It
Use the strongly-typed SDK classes to catch errors at definition time:
import google.generativeai as genai
from google.ai.generativelanguage_v1beta import FunctionDeclaration, Schema, Type
genai.configure(api_key="YOUR_GEMINI_API_KEY")
# ✅ Type-safe schema definition — catches errors before the API call
search_products_tool = FunctionDeclaration(
name="search_products",
description="Search a product database and return matching items based on criteria",
parameters=Schema(
type=Type.OBJECT,
properties={
"query": Schema(
type=Type.STRING,
description="Search keyword or phrase"
),
"category": Schema(
type=Type.STRING,
description="Product category to filter by",
enum=["electronics", "clothing", "food", "books"] # enum goes here
),
"price_range": Schema(
type=Type.OBJECT,
description="Price filter in USD",
properties={
"min": Schema(type=Type.NUMBER, description="Minimum price"),
"max": Schema(type=Type.NUMBER, description="Maximum price")
}
# No required here = both fields are optional
),
"limit": Schema(
type=Type.INTEGER, # Must be INTEGER, not "int"
description="Maximum number of results to return (default: 10)"
)
},
required=["query"] # Double-check spelling matches property names exactly
)
)
model = genai.GenerativeModel(
model_name="gemini-2.5-pro",
tools=[search_products_tool]
)
response = model.generate_content("Find me electronics under $100, show 5 results")
print(response.candidates[0].content.parts)
# Expected output:
# function_call {
# name: "search_products"
# args { query: "electronics", category: "electronics",
# price_range: { max: 100 }, limit: 5 }
# }Problem 3: Response Parsing Errors
Why This Happens
AttributeError: 'Part' object has no attribute 'function_call' or KeyError usually means you're parsing the response incorrectly. The response structure can catch developers off-guard.
How to Fix It
Build a safe parsing utility and use it consistently:
import google.generativeai as genai
genai.configure(api_key="YOUR_GEMINI_API_KEY")
def safe_parse_function_call(response):
"""
Safely parse a Function Calling response.
Returns: (tool_name, tool_args) tuple, or (None, None) if no tool was called.
"""
if not response.candidates:
print("⚠️ Empty candidates list")
return None, None
candidate = response.candidates[0]
print(f"finish_reason: {candidate.finish_reason}")
for part in candidate.content.parts:
if hasattr(part, "function_call") and part.function_call:
fc = part.function_call
tool_name = fc.name
tool_args = dict(fc.args)
print(f"✅ Tool call detected: {tool_name}({tool_args})")
return tool_name, tool_args
elif hasattr(part, "text") and part.text:
print(f"📝 Text response: {part.text}")
return None, None
# Full multi-turn Function Calling example
def run_weather_agent():
get_weather_tool = {
"name": "get_weather",
"description": "Get the current weather for a specified city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
model = genai.GenerativeModel(
model_name="gemini-2.5-pro",
tools=[get_weather_tool]
)
chat = model.start_chat()
# Step 1: Send user message
response = chat.send_message("What's the weather in Paris?")
tool_name, tool_args = safe_parse_function_call(response)
if tool_name == "get_weather":
# Step 2: Execute the tool (mock result here)
weather_result = {
"city": tool_args.get("city", "Paris"),
"temperature_c": 18,
"condition": "Partly cloudy",
"humidity_pct": 62
}
print(f"🔧 Tool executed: {weather_result}")
# Step 3: Send result back to the model
final_response = chat.send_message(
genai.protos.Content(
parts=[genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=tool_name,
response={"result": weather_result}
)
)]
)
)
print(f"🤖 Final answer: {final_response.text}")
run_weather_agent()
# Expected output:
# finish_reason: FinishReason.STOP
# ✅ Tool call detected: get_weather({'city': 'Paris'})
# 🔧 Tool executed: {'city': 'Paris', 'temperature_c': 18, ...}
# 🤖 Final answer: The current weather in Paris is partly cloudy with a temperature of 18°C...Problem 4: Wrong Tool Gets Selected
Why This Happens
When you provide multiple tools, the model may pick the wrong one if:
- Tool names or descriptions are too similar
- The tool you want to call has a weaker description than alternatives
- You haven't restricted tool selection with
allowed_function_names
How to Fix It
import google.generativeai as genai
genai.configure(api_key="YOUR_GEMINI_API_KEY")
tools = [
{
"name": "search_internal_db",
"description": "Search the internal customer database for account information",
"parameters": {
"type": "object",
"properties": {"customer_id": {"type": "string"}},
"required": ["customer_id"]
}
},
{
"name": "search_web",
"description": "Search the public internet for general information",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
}
]
# Restrict to specific tools using allowed_function_names
model = genai.GenerativeModel(
model_name="gemini-2.5-pro",
tools=tools,
tool_config={
"function_calling_config": {
"mode": "ANY",
"allowed_function_names": ["search_internal_db"] # Only this tool is available
}
}
)
response = model.generate_content("Look up account info for customer ID C-12345")
print(response.candidates[0].content.parts)
# Expected: search_internal_db is called, not search_webProblem 5: JavaScript/Node.js Function Calling Issues
Different SDKs use slightly different conventions. Here's a complete working example for Node.js:
import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// ✅ Correct Function Calling implementation for Node.js
const stockTool = {
functionDeclarations: [
{
name: "get_stock_price",
description: "Retrieves the current stock price for a given ticker symbol",
parameters: {
type: "OBJECT", // Note: uppercase in JavaScript SDK
properties: {
symbol: {
type: "STRING", // Note: uppercase
description: "Stock ticker symbol (e.g., GOOGL, AAPL, MSFT)"
}
},
required: ["symbol"]
}
}
]
};
async function runStockAgent() {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-pro",
tools: [stockTool]
});
const chat = model.startChat();
const result = await chat.sendMessage("What's Google's current stock price?");
const candidate = result.response.candidates[0];
for (const part of candidate.content.parts) {
if (part.functionCall) {
const { name, args } = part.functionCall;
console.log(`✅ Tool called: ${name}`, args);
// Execute the tool (mock result)
const toolResult = { price: 175.42, currency: "USD", change: "+1.2%" };
// Send result back
const followUp = await chat.sendMessage([{
functionResponse: {
name: name,
response: { result: toolResult }
}
}]);
console.log("🤖 Final answer:", followUp.response.text());
}
}
}
runStockAgent();
// Expected output:
// ✅ Tool called: get_stock_price { symbol: 'GOOGL' }
// 🤖 Final answer: Google's current stock price is $175.42 USD, up 1.2% today.Verification: Confirming the Fix
Run this test after any changes to confirm Function Calling is working correctly:
import google.generativeai as genai
genai.configure(api_key="YOUR_GEMINI_API_KEY")
def verify_function_calling():
"""Quick smoke test for Function Calling setup"""
test_tool = {
"name": "add_numbers",
"description": "Adds two numbers together. Use this whenever a user asks to perform addition.",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number", "description": "First number"},
"b": {"type": "number", "description": "Second number"}
},
"required": ["a", "b"]
}
}
model = genai.GenerativeModel(
model_name="gemini-2.5-pro",
tools=[test_tool],
tool_config={"function_calling_config": {"mode": "ANY"}}
)
response = model.generate_content("What is 3 + 5?")
part = response.candidates[0].content.parts[0]
assert hasattr(part, "function_call"), "❌ No function_call in response"
assert part.function_call.name == "add_numbers", "❌ Wrong tool called"
args = dict(part.function_call.args)
assert "a" in args and "b" in args, "❌ Missing expected arguments"
print(f"✅ Test passed: add_numbers(a={args['a']}, b={args['b']})")
print(f" Expected result: {args['a'] + args['b']}")
verify_function_calling()Prevention: Best Practices to Avoid Future Issues
1. Validate schemas before deployment
def validate_tool_schema(tool: dict) -> bool:
"""Basic schema validation before sending to the API"""
required_keys = ["name", "description", "parameters"]
for key in required_keys:
if key not in tool:
print(f"❌ Missing required key: '{key}'")
return False
params = tool["parameters"]
if "required" in params:
props = params.get("properties", {})
for field in params["required"]:
if field not in props:
print(f"❌ Required field '{field}' not found in properties")
return False
print(f"✅ Schema for '{tool['name']}' is valid")
return True2. Monitor finish_reason — Check for MAX_TOKENS or SAFETY blocking the response before a tool call happens
3. Add retry logic — If a tool isn't called, rephrase the message to be more explicit about what action is needed
4. Log everything — Record all function_call inputs and function_response outputs for debugging and anomaly detection
Summary
Gemini API Function Calling issues almost always come down to one of these root causes:
- The tool
descriptionis too vague for the model to know when to use it - The schema has a typo or uses an unsupported type name
- Response parsing doesn't account for the actual structure the API returns
- Multiple tools are competing and need to be restricted with
allowed_function_names
When debugging, always start with tool_config: mode: ANY to force tool usage and confirm your schema is being accepted. Then work outward to more complex multi-tool, multi-turn scenarios once the basics are verified.
For a deeper dive into Function Calling patterns and production-grade implementations, check out our Gemini API Function Calling Complete Guide.