Skip to content

tool_config mode="ANY" with automatic function calling exhausts maximum_remote_calls and returns empty text, with no warning #2839

Description

@arunpshankar

Summary

FunctionCallingConfig(mode="ANY") combined with automatic function
calling produces a request that consumes the whole remote-call budget and
returns empty text, with no error and no warning. mode="ANY"
obliges the model to emit a function call on every turn; AFC dutifully
executes each one and asks again; the loop only ends when
maximum_remote_calls runs out, at which point there is no natural
language answer to return because the model was never permitted to give
one.

The combination is detectable at config time, and the library already
warns about two neighbouring misconfigurations — just not this one.

Where

google/genai/_extra_utils.py, at ccbc6c5. Both existing guards live
in the same function:

  if (... maximum_remote_calls is not None
      and int(...maximum_remote_calls) <= 0):
    logger.warning(
        'max_remote_calls in automatic_function_calling_config'
        f' {...} is less than or equal to 0. Disabling automatic function'
        ' calling. Please set max_remote_calls to a positive integer.'
    )
    return True

  if (config_model.automatic_function_calling.disable
      and ...maximum_remote_calls is not None
      and 'maximum_remote_calls' in ...model_fields_set
      and int(...maximum_remote_calls) > 0):
    logger.warning(
        '`automatic_function_calling.disable` is set to `True`. And'
        ' `automatic_function_calling.maximum_remote_calls` is a positive'
        ' number ...'
    )

There is no equivalent check for tool_config.function_calling_config.mode == ANY while AFC is enabled.

Why it matters

Every symptom points away from the cause. You get a successful response
object, finish_reason looks ordinary, and the text is empty — so the
natural conclusion is that the model failed to answer, or the prompt is
bad, or the tools are wrong. The actual cause is two settings that are
each individually reasonable and only interact badly together. The
logger.info('Reached max remote calls...') that does fire reads as
routine, because it is the same message a legitimately long tool chain
produces.

Reproduce

from google import genai
from google.genai import types

def get_weather(city: str) -> str:
    return f"sunny in {city}"

client = genai.Client()
resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="What is the weather in Paris?",
    config=types.GenerateContentConfig(
        tools=[get_weather],
        tool_config=types.ToolConfig(
            function_calling_config=types.FunctionCallingConfig(mode="ANY")
        ),
    ),
)
print(repr(resp.text))     # empty

Suggested fix

A warning at config time, in the same place as the two existing ones —
something to the effect that mode="ANY" with automatic function calling
enabled will consume maximum_remote_calls and return no text, and that
mode="AUTO" is wanted if the model should be allowed to answer.

I am raising this as an issue rather than sending the patch because
whether you want a warning here is your call: mode="ANY" is a
legitimate setting, and someone driving the loop manually with AFC
disabled has a perfectly good reason to use it. If you would like the
warning, I am happy to send a PR.

Environment

  • google-genai: read from source at ccbc6c5 (current main); latest
    release at time of writing is v2.17.0
  • Python: 3.13

Metadata

Metadata

Assignees

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions