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
Summary
FunctionCallingConfig(mode="ANY")combined with automatic functioncalling 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_callsruns out, at which point there is no naturallanguage 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, atccbc6c5. Both existing guards livein the same function:
There is no equivalent check for
tool_config.function_calling_config.mode == ANYwhile AFC is enabled.Why it matters
Every symptom points away from the cause. You get a successful response
object,
finish_reasonlooks ordinary, and the text is empty — so thenatural 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 asroutine, because it is the same message a legitimately long tool chain
produces.
Reproduce
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 callingenabled will consume
maximum_remote_callsand return no text, and thatmode="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 alegitimate 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
ccbc6c5(currentmain); latestrelease at time of writing is v2.17.0