Skip to content

RecursionError in _to_gemini_schema() when processing circular $ref in $defs #3870

@foivos-all

Description

@foivos-all

Describe the bug
The _to_gemini_schema() function in google.adk.tools._gemini_schema_util triggers a RecursionError when processing JSON Schemas with circular $ref references in $defs. The _dereference_schema() helper function recursively resolves these references without detecting circular dependencies, causing infinite recursion.

To Reproduce

  • Minimal code to reproduce the issue:
    from google.adk.tools._gemini_schema_util import _to_gemini_schema
    
    # Schema with circular reference: Attendee -> alternateAttendee -> Attendee
    schema_with_circular_refs = {
        "type": "object",
        "properties": {
            "body": {
                "type": "object",
                "properties": {
                    "attendees": {
                        "type": "array",
                        "items": {"$ref": "#/$defs/Attendee"}
                    },
                    "end": {"$ref": "#/$defs/DateTimeTimeZone"}
                }
            }
        },
        "$defs": {
            "DateTimeTimeZone": {
                "type": "object",
                "properties": {
                    "dateTime": {"type": "string"},
                    "timeZone": {"type": "string"}
                }
            },
            "Attendee": {
                "type": "object",
                "properties": {
                    "proposedNewTime": {
                        "type": "object",
                        "properties": {
                            "start": {"$ref": "#/$defs/DateTimeTimeZone"},
                            "end": {"$ref": "#/$defs/DateTimeTimeZone"}
                        }
                    },
                    "alternateAttendee": {"$ref": "#/$defs/Attendee"}
                }
            }
        },
        "required": ["body"]
    }
    
    # This will trigger a RecursionError
    gemini_schema = _to_gemini_schema(schema_with_circular_refs)
    
  • Steps to reproduce:
    1. Install google-adk and google-genai
    2. Run the code above
    3. Observe RecursionError: maximum recursion depth exceeded

Expected behavior

  • The function should either:

    1. Detect circular references and raise a clear, descriptive error (e.g., ValueError: Circular reference detected in schema at path ...)
    2. Handle circular references gracefully by tracking visited definitions during dereferencing
  • Stack trace:

       [...]
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 94, in _resolve_refs
         return _resolve_refs(resolved)
                ^^^^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 94, in _resolve_refs
         return _resolve_refs(resolved)
                ^^^^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 94, in _resolve_refs
         return _resolve_refs(resolved)
                ^^^^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       [Previous line repeated 1 more time]
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 94, in _resolve_refs
         return _resolve_refs(resolved)
                ^^^^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
       File "/home/ubuntu/venv/lib/python3.12/site-packages/google/adk/tools/_gemini_schema_util.py", line 100, in _resolve_refs
         return {key: _resolve_refs(value) for key, value in sub_schema.items()}
                      ^^^^^^^^^^^^^^^^^^^^
     RecursionError: maximum recursion depth exceeded
    

Desktop (please complete the following information):

  • OS: Linux (Ubuntu)
  • Python version(python -V): Python 3.12.3
  • ADK version(pip show google-adk):
    • google-adk==1.20.0
    • google-genai==1.54.0

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used(e.g. gemini-2.5-pro): N/A (error occurs during schema conversion, before model invocation)

Additional Context
This issue was discovered while building an MCP server tool that uses schemas with legitimate circular references for recursive data structures (for example, attendees with alternate attendees)

  • The root cause appears to be in _dereference_schema(), which currently does not perform cycle detection when recursively resolving $ref pointers in $defs
  • A previous attempt was made to fix this in google-genai, but that change could not be merged because the affected file (types.py) is dynamically generated
  • I was advised to raise an issue (and potentially a PR) here; once I figure out an appropriate approach, I am happy to follow up with a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    mcp[Component] Issues about MCP supporttools[Component] This issue is related to tools

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions