Skip to content

[Bug] GoogleGenaiSamplingHandler: Pydantic title fields in tool schemas cause MALFORMED_FUNCTION_CALL on Gemini 2.5 Flash #3860

@strawgate

Description

@strawgate

Description

Gemini 2.5 Flash consistently returns MALFORMED_FUNCTION_CALL when a function declaration's parameters_json_schema contains "title" fields — which Pydantic adds by default to every field and object in .json_schema() output.

This affects all ctx.sample(result_type=...) calls when using the GoogleGenaiSamplingHandler with Gemini 2.5 Flash, because FastMCP creates a final_response tool from the Pydantic schema. The sample_impl function calls compress_schema(schema, prune_titles=True) for validation purposes, but the actual tool schema sent to Gemini goes through _convert_tool_to_google_genai which passes tool.inputSchema directly — with titles intact.

Root Cause

Isolated via systematic testing:

# This schema WORKS (no titles):
{"properties": {"items": {"items": {"properties": {"x": {"type": "string"}}, ...}}}}

# This schema FAILS (with titles):
{"properties": {"items": {"title": "Items", "items": {"title": "Item", "properties": {"x": {"title": "X", "type": "string"}}, ...}}}}
  • $ref/$defs alone: works fine
  • description fields: works fine
  • title on any property or object: 100% failure on Gemini 2.5 Flash
  • Gemini 2.5 Pro: unaffected (handles titles correctly)
  • Gemini JSON mode (response_json_schema): unaffected (handles titles correctly)

Reproduction

from google import genai
from google.genai import types
from pydantic import BaseModel, TypeAdapter

class Item(BaseModel):
    index: int
    label: str

class Result(BaseModel):
    items: list[Item]

# This fails on gemini-2.5-flash:
schema = TypeAdapter(Result).json_schema()  # has "title" fields
tool = types.Tool(function_declarations=[
    types.FunctionDeclaration(name="respond", description=".", parameters_json_schema=schema)
])
resp = genai.Client().models.generate_content(
    model="gemini-2.5-flash",
    contents="Classify: apple, banana",
    config=types.GenerateContentConfig(
        tools=[tool],
        tool_config=types.ToolConfig(
            function_calling_config=types.FunctionCallingConfig(mode="ANY")
        ),
    ),
)
print(resp.candidates[0].finish_reason)  # MALFORMED_FUNCTION_CALL

# Stripping titles fixes it:
def strip_titles(obj):
    if isinstance(obj, dict):
        return {k: strip_titles(v) for k, v in obj.items() if k != "title"}
    if isinstance(obj, list):
        return [strip_titles(i) for i in obj]
    return obj

schema_clean = strip_titles(schema)  # same schema without "title" keys
# Using schema_clean → FinishReason.STOP (works)

Suggested Fix

In _convert_tool_to_google_genai (google_genai.py line ~150), strip title fields from the schema before passing to Gemini:

def _convert_tool_to_google_genai(tool: MCPTool) -> GoogleTool:
    schema = tool.inputSchema
    # Strip 'title' fields — Gemini 2.5 Flash produces MALFORMED_FUNCTION_CALL
    # when schemas contain Pydantic's auto-generated title annotations
    schema = _strip_titles(schema)
    return GoogleTool(
        function_declarations=[
            FunctionDeclaration(
                name=tool.name,
                description=tool.description or "",
                parameters_json_schema=schema,
            )
        ]
    )

Note: compress_schema(schema, prune_titles=True) already exists in run.py and does this — but it's only applied for validation, not for the schema sent to Gemini.

Affected Versions

  • fastmcp 3.2.3
  • google-genai 1.72.0
  • Gemini 2.5 Flash (affected), Gemini 2.5 Pro (not affected)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working. Reports of errors, unexpected behavior, or broken functionality.clientRelated to the FastMCP client SDK or client-side functionality.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions