Skip to content

AttributeError: 'NoneType' object has no attribute 'get' when using with_structured_output(dict_schema, method='function_calling') with a titleless dict schema #1807

Description

@awg66

Description

with_structured_output(schema, method="function_calling") raises AttributeError: 'NoneType' object has no attribute 'get' inside _prepare_request when schema is a dict that does not have a top-level "title" key.

Minimal Repro

from langchain_google_genai import ChatGoogleGenerativeAI

llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash-001")

# No "title" key — this crashes
schema = {
    "type": "object",
    "properties": {
        "answer": {"type": "string"},
        "score": {"anyOf": [{"type": "number"}, {"type": "null"}]},
    },
    "required": ["answer"],
}

structured = llm.with_structured_output(schema, method="function_calling")
structured.invoke("What weighs more, a pound of bricks or a pound of feathers?")

Error:

File ".../langchain_google_genai/_function_utils.py", line 404, in _format_to_genai_function_declaration
    elif "parameters" in tool and tool["parameters"].get("properties"):
                                  ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'

A dict schema with "title" works fine:

schema = {"title": "MyOutput", "type": "object", "properties": {...}, "required": [...]}

Root Cause

Two problems compound in bind_tools + _prepare_request:

Problem 1 — bind_tools fallback produces a broken FunctionDeclaration

# ChatGoogleGenerativeAI.bind_tools (chat_models.py ~line 66)
try:
    formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
except Exception:
    formatted_tools = [tool_to_dict(t) for t in convert_to_genai_function_declarations(tools)]

convert_to_openai_tool raises ValueError: must have a top-level 'title' key for titleless dicts. The except Exception fallback calls convert_to_genai_function_declarations([titleless_schema]).

Inside _format_to_genai_function_declaration, the schema has no "name", "description", or "parameters" keys, so it falls into the else branch, sets function["parameters"] = {}, and _format_dict_to_function_declaration can't find a name → FunctionDeclaration(name="MISSING_NAME", parameters=None).

tool_to_dict serialises this as:

{"function_declarations": [{"name": "MISSING_NAME", "parameters": null}]}

Problem 2 — Missing None guard in _format_to_genai_function_declaration

When _prepare_request calls convert_to_genai_function_declarations a second time on the stored {"function_declarations": [...]} tools, it iterates over the function declarations and calls _format_to_genai_function_declaration({"name": "MISSING_NAME", "parameters": null}).

Line 404 (_function_utils.py):

elif "parameters" in tool and tool["parameters"].get("properties"):

"parameters" in tool is True (the key exists, value is null), but tool["parameters"] is None — calling .get() on it crashes.

Fix

Either problem fixed independently would prevent the crash. The minimal correct fix is adding a None guard:

# _function_utils.py line ~404
elif "parameters" in tool and tool["parameters"] is not None and tool["parameters"].get("properties"):

A more complete fix would also make the fallback path in bind_tools raise a clearer error (or handle the missing-title case) instead of silently producing FunctionDeclaration(name="MISSING_NAME", parameters=None).

Environment

  • langchain-google-genai version: checked against current main
  • Python 3.12

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions