GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/API / SDK
API / SDK/2026-04-06Intermediate

Gemini Function Calling Isn't Firing: Five Symptoms and Their Causes

Fix Gemini API Function Calling issues fast. This guide covers the most common causes — bad schemas, wrong model, parse errors, and tool selection problems — with step-by-step solutions and working code examples.

troubleshooting82error15fix10function-calling20gemini-api277

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:

  1. Your app sends a tool definition (schema) + user message to the API
  2. The model decides to call a tool and returns a function_call in the response
  3. Your app executes the tool and sends the result back as a function_response
  4. 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 description field 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_choice override: 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 tools
  • ANY: Model must call one of the provided tools
  • NONE: 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 required field names that don't match properties keys
  • Unsupported type names (e.g., "int" instead of "integer")
  • Missing type definitions in nested objects
  • Putting enum values 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_web

Problem 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 True

2. 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 description is 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.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API / SDK2026-05-31
Why Gemini API Throws 'Unsupported MIME type' and How to Fix It
The 'Unsupported MIME type' error from the Gemini API has three distinct causes: a misspelled MIME string, an octet-stream upload, and a genuinely unsupported format. Here is how to tell them apart with code that actually works.
API / SDK2026-05-30
Why Gemini 2.5 Pro Rejects thinkingBudget: 0 (and How to Fix It)
Setting thinkingBudget to 0 on Gemini 2.5 Pro returns a 400 INVALID_ARGUMENT error. Here is why the per-model thinking budget ranges differ, how to minimize thinking on Pro the right way, and when to switch to Flash, with Python and JavaScript examples.
API / SDK2026-04-14
Veo API Not Working? Common Errors and How to Fix Them
Troubleshoot common Veo API errors including polling implementation mistakes, safety filter rejections, quota exceeded, and video file download failures. With working Python code examples.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →