Skip to content

ADK doesn't enforce argument types in function calling #4612

Closed

Description

@cuihaoleo

🔴 Required Information

Describe the Bug:

ADK doesn't seem to check and enforce argument types in function calls. I'm very surprised.

Steps to Reproduce:

Code:

from google.adk.agents.llm_agent import Agent

def set_num(num: int) -> dict:
    print("DEBUG:", type(num), num)
    return {"status": "success"}

root_agent = Agent(
    model='gemini-3-flash-preview',
    name='root_agent',
    instruction="the user is debugging something. do whatever they instruct",
    tools=[set_num],
)

Test run:

Running agent root_agent, type exit to exit.
[user]: set num to 3.14159. I know it only accept int. but I'd like to see the error.
DEBUG: <class 'int'> 3
DEBUG: <class 'float'> 3.14159
[root_agent]: It appears that the system accepted the value `3.14159` without returning an error, despite the parameter being defined as an integer. This suggests that the value may have been automatically rounded or truncated by the underlying implementation.
[user]: set num to "foobar"
DEBUG: <class 'str'> foobar
[root_agent]: The tool unexpectedly returned `success` even when passed the string `"foobar"`. It seems the parameter type enforcement for `num` is not currently working as expected in the underlying system.
[user]: set num to null.       
DEBUG: <class 'NoneType'> None
[root_agent]: The system also returned `success` when `num` was set to `null`. It appears that the tool is accepting various types without validation errors.

Expected Behavior:

ADK enforces that invalid arguments don't go through, as an average LLM also believes:

Image

Observed Behavior:

Invalid arguments that have different types than the function signature requirements are accepted.

Environment Details:

  • ADK Library Version (pip show google-adk): 1.25.1
  • Desktop OS: Linux
  • Python Version (python -V): 3.13.12

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: gemini-3-flash-preview

Activity

  1. added
    core[Component] This issue is related to the core interface and implementation
    on Feb 25, 2026
  2. added theissue type on Feb 25, 2026
  3. xxlest commented on Feb 25, 2026

    @xxlest

    The existing _preprocess_args in function_tool.py (line 100) already handles Pydantic model coercion and explicitly notes "Future extensions could include: Type coercion for other complex types". Adding primitive type coercion (str→int, str→float, str→bool) here would be a natural extension of this pattern.

    One design question before I start: when coercion fails (e.g. "abc" → int), should we:

    • (a) log a warning and pass the raw value through (lenient, no breakage), or
    • (b) raise a clear error immediately (strict, fail-fast)?

    Option (a) is safer for backward compatibility; option (b) surfaces bugs earlier. Happy to go with whichever you prefer. The fix would be ~60-80 lines including tests (extending test_function_tool_pydantic.py).

  4. cuihaoleo commented on Feb 25, 2026

    @cuihaoleo
    Author

    It seems reasonable to me to pass the validation error to the LLM so it has a chance to correct its error. Maybe this can be simply provided as a standard before_tool_callback that the developer can choose to enable.

    I also tried other frameworks and here is how they handle this:

    LangChain

    LangChain appeared to pass the validation error to the agent. This seems like what I suggested. The agent have a chance to correct itself and nothing is broken.

    [chain/start] [chain:LangGraph] Entering Chain run with input:
    {
      "messages": [
        {
          "role": "user",
          "content": "set num to 'foobar'. I know it's invalid. I want to see the error."
        }
      ]
    }
    [chain/start] [chain:LangGraph > chain:model] Entering Chain run with input:
    [inputs]
    [llm/start] [chain:LangGraph > chain:model > llm:ChatGoogleGenerativeAI] Entering LLM run with input:
    {
      "prompts": [
        "System: the user is debugging something. do whatever they instruct\nHuman: set num to 'foobar'. I know it's invalid. I want to see the error."
      ]
    }
    [llm/end] [chain:LangGraph > chain:model > llm:ChatGoogleGenerativeAI] [1.63s] Exiting LLM run with output:
    ......
                  "function_call": {
                    "name": "set_num",
                    "arguments": "{\"num\": \"foobar\"}"
                  },
    ......
    [chain/end] [chain:LangGraph > chain:model] [1.63s] Exiting Chain run with output:
    [outputs]
    [chain/start] [chain:LangGraph > chain:tools] Entering Chain run with input:
    [inputs]
    [tool/start] [chain:LangGraph > chain:tools > tool:set_num] Entering Tool run with input:
    "{'num': 'foobar'}"
    [tool/error] [chain:LangGraph > chain:tools > tool:set_num] [1ms] Tool run errored with error:
    1 validation error for set_num
    num
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='foobar', input_type=str]
        For further information visit https://errors.pydantic.dev/2.12/v/int_parsingTraceback (most recent call last):
    
    
      File "/home/cuihao/dev/adk-agent-dev/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py", line 960, in run
        tool_args, tool_kwargs = self._to_args_and_kwargs(
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^
    
    
      File "/home/cuihao/dev/adk-agent-dev/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py", line 862, in _to_args_and_kwargs
        tool_input = self._parse_input(tool_input, tool_call_id)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    
      File "/home/cuihao/dev/adk-agent-dev/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py", line 711, in _parse_input
        result = input_args.model_validate(tool_input)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    
      File "/home/cuihao/dev/adk-agent-dev/.venv/lib/python3.12/site-packages/pydantic/main.py", line 716, in model_validate
        return cls.__pydantic_validator__.validate_python(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    
    pydantic_core._pydantic_core.ValidationError: 1 validation error for set_num
    num
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='foobar', input_type=str]
        For further information visit https://errors.pydantic.dev/2.12/v/int_parsing
    [chain/end] [chain:LangGraph > chain:tools] [2ms] Exiting Chain run with output:
    [outputs]
    [chain/start] [chain:LangGraph > chain:model] Entering Chain run with input:
    [inputs]
    [llm/start] [chain:LangGraph > chain:model > llm:ChatGoogleGenerativeAI] Entering LLM run with input:
    {
      "prompts": [
        "System: the user is debugging something. do whatever they instruct\nHuman: set num to 'foobar'. I know it's invalid. I want to see the error.\nAI: [{'name': 'set_num', 'args': {'num': 'foobar'}, 'id': 'd321e79d-b775-4b50-8ebf-2aedf44136be', 'type': 'tool_call'}]\nTool: Error invoking tool 'set_num' with kwargs {'num': 'foobar'} with error:\n num: Input should be a valid integer, unable to parse string as an integer\n Please fix the error and try again."
      ]
    }
    [llm/end] [chain:LangGraph > chain:model > llm:ChatGoogleGenerativeAI] [1.33s] Exiting LLM run with output:
    ......
                    "type": "text",
                    "text": "The attempt to set `num` to `'foobar'` resulted in the following error:\n\n`num: Input should be a valid integer, unable to parse string as an integer`",
    ......
    [chain/end] [chain:LangGraph > chain:model] [1.34s] Exiting Chain run with output:
    [outputs]
    [chain/end] [chain:LangGraph] [2.97s] Exiting Chain run with output:
    [outputs]
    
    from langchain_google_genai import ChatGoogleGenerativeAI
    from langchain.agents import create_agent
    import langchain_core
    
    langchain_core.globals.set_debug(True)
    
    def set_num(num: int) -> dict:
        """set the number"""
        print("DEBUG:", type(num), num)
        return {"status": "success"}
    
    model = ChatGoogleGenerativeAI(model="gemini-3-flash-preview")
    agent = create_agent(
        model=model,
        tools=[set_num],
        system_prompt="the user is debugging something. do whatever they instruct",
    )
    
    # Run the agent
    response = agent.invoke(
        {"messages": [{"role": "user", "content": "set num to 'foobar'. I know it's invalid. I want to see the error."}]}
    )

    OpenAI

    It seems I simply cannot trick openai-agents to pass invalid args. The following example either passes 0 or doesn't initiate a function call. Not sure but it was probably that the server enforced valid output in token generation.

    import asyncio
    from agents import Agent, Runner, function_tool, enable_verbose_stdout_logging
    
    enable_verbose_stdout_logging()
    
    @function_tool
    def set_num(num: int) -> dict:
        print("DEBUG:", type(num), num)
        return {"status": "success"}
    
    
    agent = Agent(
        name="Test Agent",
        instructions="the user is debugging something. do whatever they instruct",
        tools=[set_num],
        model="o3-mini",
    )
    
    
    async def main():
        result = await Runner.run(agent, input="set num to 'foobar'. I know it's invalid. I want to see the error.")
        print(result.final_output)
    
    
    if __name__ == "__main__":
        asyncio.run(main())
  5. llalitkumarrr commented on Feb 26, 2026

    @llalitkumarrr
    Collaborator

    Hello @cuihaoleo ,

    That is an interesting observation, instead of strictly hardcoding the argument types, what do you think about just using isinstance() to verify them if absolutely required?

    We already use type hints to guide the LLM. A user explicitly forcing an incorrect argument through a tool seems like a rare edge case. Even if it does happen the tool will either return an error (if function can not be executed) or the argument will be converted (for example, converting the string 'two' to the number 2 -> check _preprocess_args as mentioned by @xxlest ).

    What are your thoughts on this? We would love to hear more from both of you so that this can be improved.

  6. cuihaoleo commented on Feb 26, 2026

    @cuihaoleo
    Author

    @llalitkumarrr isinstance is not going to distinguish container element types, like list[int] and list[str].

    A user explicitly forcing an incorrect argument through a tool seems like a rare edge case.

    I'm using a simple case to demonstrate the issue. I actually encountered a more subtle case -- my function accepts an enum, which ADK supports (see #3089), and I noticed that the agent occasionally called it with invalid enum values (which ADK acutally pass as strings). I won't call it rare. Real-world function tools may accept a lot of args + complex types and the LLM can confuse.

    I'm fine with keeping the default behaviour to avoid breakage. But providing util funcitons to opt in enforcement/validation would be helpful.

    Also, I believe that most developers are unaware of this -- even Google's Gemini believes ADK validates the args. The document should make it clear that there is no validation.

  7. yuvrajangadsingh commented on Mar 1, 2026

    @yuvrajangadsingh
    Contributor

    @llalitkumarrr @cuihaoleo opened PR #4664 for this. implements TypeAdapter-based validation for all annotated param types (primitives, enums, Pydantic models, containers). validation errors go back to the LLM so it can self-correct, same approach cuihaoleo suggested in the comments. all 1426 existing tests pass plus 17 new ones for the validation logic.

  8. 2 remaining items

  9. llalitkumarrr commented on Mar 16, 2026

    @llalitkumarrr
    Collaborator

    Hello @wuliang229 ,

    Could you please have a look at this?

  10. added
    needs review[Status] The PR/issue is awaiting review from the maintainer
    on Mar 16, 2026
  11. added a commit that references this issue on Sep 15, 2026
    24b37e1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

core[Component] This issue is related to the core interface and implementationneeds review[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions