MALFORMED_FUNCTION_CALL seems to be easily triggered by the model (Gemini) due to writing todo. #45

Open
opened 2026-02-16 08:19:36 -05:00 by yindo · 10 comments
Owner

Originally created by @XinyueZ on GitHub (Sep 19, 2025).

Originally assigned to: @mdrxy on GitHub.

Currently, I have found that the frequency of this issue occurring on write todo is quite high.


      AIMessage(
         content='',
         additional_kwargs={},
         response_metadata={
            'is_blocked': False,
            'safety_ratings': [],
            'usage_metadata': {
               'prompt_token_count': 5553,
               'total_token_count': 5553,
               'prompt_tokens_details': [{'modality': 1, 'token_count': 5553}],
               'candidates_token_count': 0,
               'thoughts_token_count': 0,
               'cached_content_token_count': 0,
               'cache_tokens_details': [],
               'candidates_tokens_details': []
            },
            'finish_reason': 'MALFORMED_FUNCTION_CALL',
            'finish_message': 'Malformed function call: print(default_api.write_todos(todos=[\n    default_api.WriteTodosTodos(content=\'Perform web search for "CrewAI vs LangGraph comparison"\', status=\'pending\'),\n    default_api.WriteTodosTodos(content=\'Analyze search results for key information on architecture, scalability, integration, community, documentation, licensing, cost, and use cases for both CrewAI and LangGraph.\', status=\'pending\'),\n    default_api.WriteTodosTodos(content=\'Perform web search for "CrewAI architecture performance integration"\', status=\'pending\'),\n    default_api.WriteTodosTodos(content=\'Analyze search results for key information on architecture, scalability, integration, community, documentation, licensing, cost, and use cases for both CrewAI and LangGraph.\', status=\'pending\'),\n    default_api.WriteTodosTodos(content=\'Perform web search for "LangGraph architecture performance integration"\', status=\'pending\'),\n    default_api.WriteTodosTodos(content=\'Analyze search results for key information on architecture, scalability, integration, community, documentation, licensing, cost, and use cases for both CrewAI and LangGraph.\', status=\'pending\'),\n    default_api.WriteTodosTodos(content=\'Consolidate all findings and generate a comprehensive report comparing CrewAI and LangGraph.\', status=\'pending\')\n]))',
            'model_name': 'gemini-2.5-flash'
         },
         id='run--8a36437b-6e83-4081-881e-e4bb174b752d-0',
         usage_metadata={'input_tokens': 5553, 'output_tokens': 0, 'total_tokens': 5553, 'input_token_details': {'cache_read': 0}}
      )
   ],


Originally created by @XinyueZ on GitHub (Sep 19, 2025). Originally assigned to: @mdrxy on GitHub. Currently, I have found that the frequency of this issue occurring on write todo is quite high. ```python │ │ AIMessage( │ │ │ content='', │ │ │ additional_kwargs={}, │ │ │ response_metadata={ │ │ │ │ 'is_blocked': False, │ │ │ │ 'safety_ratings': [], │ │ │ │ 'usage_metadata': { │ │ │ │ │ 'prompt_token_count': 5553, │ │ │ │ │ 'total_token_count': 5553, │ │ │ │ │ 'prompt_tokens_details': [{'modality': 1, 'token_count': 5553}], │ │ │ │ │ 'candidates_token_count': 0, │ │ │ │ │ 'thoughts_token_count': 0, │ │ │ │ │ 'cached_content_token_count': 0, │ │ │ │ │ 'cache_tokens_details': [], │ │ │ │ │ 'candidates_tokens_details': [] │ │ │ │ }, │ │ │ │ 'finish_reason': 'MALFORMED_FUNCTION_CALL', │ │ │ │ 'finish_message': 'Malformed function call: print(default_api.write_todos(todos=[\n default_api.WriteTodosTodos(content=\'Perform web search for "CrewAI vs LangGraph comparison"\', status=\'pending\'),\n default_api.WriteTodosTodos(content=\'Analyze search results for key information on architecture, scalability, integration, community, documentation, licensing, cost, and use cases for both CrewAI and LangGraph.\', status=\'pending\'),\n default_api.WriteTodosTodos(content=\'Perform web search for "CrewAI architecture performance integration"\', status=\'pending\'),\n default_api.WriteTodosTodos(content=\'Analyze search results for key information on architecture, scalability, integration, community, documentation, licensing, cost, and use cases for both CrewAI and LangGraph.\', status=\'pending\'),\n default_api.WriteTodosTodos(content=\'Perform web search for "LangGraph architecture performance integration"\', status=\'pending\'),\n default_api.WriteTodosTodos(content=\'Analyze search results for key information on architecture, scalability, integration, community, documentation, licensing, cost, and use cases for both CrewAI and LangGraph.\', status=\'pending\'),\n default_api.WriteTodosTodos(content=\'Consolidate all findings and generate a comprehensive report comparing CrewAI and LangGraph.\', status=\'pending\')\n]))', │ │ │ │ 'model_name': 'gemini-2.5-flash' │ │ │ }, │ │ │ id='run--8a36437b-6e83-4081-881e-e4bb174b752d-0', │ │ │ usage_metadata={'input_tokens': 5553, 'output_tokens': 0, 'total_tokens': 5553, 'input_token_details': {'cache_read': 0}} │ │ ) │ ], ```
yindo added the bugintegrationdeepagentsexternal labels 2026-02-16 08:19:36 -05:00
Author
Owner

@riadzeitounn commented on GitHub (Sep 19, 2025):

Happening to me alot, although I tell the model not to write code, but gemini 2.5 pro for some reason always does that

@riadzeitounn commented on GitHub (Sep 19, 2025): Happening to me alot, although I tell the model not to write code, but gemini 2.5 pro for some reason always does that
Author
Owner

@XinyueZ commented on GitHub (Sep 21, 2025):

up to repo owner

@XinyueZ commented on GitHub (Sep 21, 2025): up to repo owner
Author
Owner

@haadirakhangi commented on GitHub (Oct 6, 2025):

Facing the same issue. Is there any way to fix it?

@haadirakhangi commented on GitHub (Oct 6, 2025): Facing the same issue. Is there any way to fix it?
Author
Owner

@nhuang-lc commented on GitHub (Oct 21, 2025):

@XinyueZ it looks like you were running into this with gemini-2.5-flash, were there other models where you were seeing this behavior as well?

@riadzeitounn looks like gemini-2.5-pro also had this issue

@haadirakhangi was your issue with gemini as well?

Personally I usually test with the latest OpenAI (gpt-4.1, gpt-5) / Anthropic models (sonnet 3-7 and up, recently 4 and 4-5) and I don't see this issue. I'll investigate this.

It might be good to add retries automatically to try and mitigate this issue

@nhuang-lc commented on GitHub (Oct 21, 2025): @XinyueZ it looks like you were running into this with gemini-2.5-flash, were there other models where you were seeing this behavior as well? @riadzeitounn looks like gemini-2.5-pro also had this issue @haadirakhangi was your issue with gemini as well? Personally I usually test with the latest OpenAI (gpt-4.1, gpt-5) / Anthropic models (sonnet 3-7 and up, recently 4 and 4-5) and I don't see this issue. I'll investigate this. It might be good to add retries automatically to try and mitigate this issue
Author
Owner

@XinyueZ commented on GitHub (Oct 22, 2025):

Retry is a good idea, but where? Internal or external at use side?

@XinyueZ it looks like you were running into this with gemini-2.5-flash, were there other models where you were seeing this behavior as well?

@riadzeitounn looks like gemini-2.5-pro also had this issue

@haadirakhangi was your issue with gemini as well?

Personally I usually test with the latest OpenAI (gpt-4.1, gpt-5) / Anthropic models (sonnet 3-7 and up, recently 4 and 4-5) and I don't see this issue. I'll investigate this.

It might be good to add retries automatically to try and mitigate this issue

@XinyueZ commented on GitHub (Oct 22, 2025): Retry is a good idea, but where? Internal or external at use side? > @XinyueZ it looks like you were running into this with gemini-2.5-flash, were there other models where you were seeing this behavior as well? > > @riadzeitounn looks like gemini-2.5-pro also had this issue > > @haadirakhangi was your issue with gemini as well? > > > Personally I usually test with the latest OpenAI (gpt-4.1, gpt-5) / Anthropic models (sonnet 3-7 and up, recently 4 and 4-5) and I don't see this issue. I'll investigate this. > > It might be good to add retries automatically to try and mitigate this issue > >
Author
Owner

@haadirakhangi commented on GitHub (Oct 22, 2025):

Yeah, I ran into the same problem with Gemini. It seems like Gemini can't handle tools names that includes hypens -. I noticed this while using an MCP server that followed that naming convention.

@haadirakhangi was your issue with gemini as well?

Personally I usually test with the latest OpenAI (gpt-4.1, gpt-5) / Anthropic models (sonnet 3-7 and up, recently 4 and 4-5) and I don't see this issue. I'll investigate this.

It might be good to add retries automatically to try and mitigate this issue

@haadirakhangi commented on GitHub (Oct 22, 2025): Yeah, I ran into the same problem with Gemini. It seems like Gemini can't handle tools names that includes hypens `-`. I noticed this while using an MCP server that followed that naming convention. > [@haadirakhangi](https://github.com/haadirakhangi) was your issue with gemini as well? > > Personally I usually test with the latest OpenAI (gpt-4.1, gpt-5) / Anthropic models (sonnet 3-7 and up, recently 4 and 4-5) and I don't see this issue. I'll investigate this. > > It might be good to add retries automatically to try and mitigate this issue
Author
Owner

@deepak-stratforge commented on GitHub (Nov 4, 2025):

Both gemini-2.5-flash-lite and gemini-2.5-pro work fine for me, but gemini-2.5-flash fails to generate the write_todos tool call

@deepak-stratforge commented on GitHub (Nov 4, 2025): Both gemini-2.5-flash-lite and gemini-2.5-pro work fine for me, but gemini-2.5-flash fails to generate the write_todos tool call
Author
Owner

@josiahcoad commented on GitHub (Dec 8, 2025):

Facing the same.

@josiahcoad commented on GitHub (Dec 8, 2025): Facing the same.
Author
Owner

@mabossert commented on GitHub (Feb 8, 2026):

We're hitting this with Gemini 3 Flash Preview via Google Vertex AI (google_genai provider). The model sends todos as a JSON string instead of a parsed list:

1 validation error for write_todos - todos: Input should be a valid list
[type=list_type, input_value='[{"content": "Search for...', input_type=str]

The error originates in pydantic validation inside StructuredTool._parse_input() — the todos field expects list[Todo] but receives a str containing valid JSON.

Our workaround: We monkey-patch TodoListMiddleware.__init__ to wrap the write_todos tool's invoke/ainvoke with input coercion. The wrapper intercepts both flat dict and ToolCall-format input, parses todos from string to list via json.loads(), and passes the coerced input to the original method. If parsing fails, it passes through unchanged so pydantic gives its normal error.

Key implementation detail: StructuredTool is a pydantic BaseModel, so replacing invoke/ainvoke requires object.__setattr__() to bypass pydantic's __setattr__.

# Simplified version of our patch
def _coerce_todos_input(tool_input):
    # Handle ToolCall format: {"args": {"todos": "[...]"}, ...}
    args = tool_input.get("args")
    if isinstance(args, dict) and "todos" in args:
        todos = args["todos"]
        if isinstance(todos, str):
            parsed = json.loads(todos)
            if isinstance(parsed, list):
                return {**tool_input, "args": {**args, "todos": parsed}}
    # Handle flat format: {"todos": "[...]"}
    todos = tool_input.get("todos")
    if isinstance(todos, str):
        parsed = json.loads(todos)
        if isinstance(parsed, list):
            return {**tool_input, "todos": parsed}
    return tool_input

Suggested upstream fix: Add coercion logic to the write_todos tool definition itself (or its args_schema) so that string-encoded lists are accepted. This would be a one-line validator on the Todo list field. Happy to contribute a PR if that approach works for you.

Environment:

  • deepagents 0.3.12
  • langchain-core 0.3.x
  • Model: Gemini 3 Flash Preview via Vertex AI
  • Python 3.13
@mabossert commented on GitHub (Feb 8, 2026): We're hitting this with **Gemini 3 Flash Preview** via Google Vertex AI (`google_genai` provider). The model sends `todos` as a JSON **string** instead of a parsed list: ``` 1 validation error for write_todos - todos: Input should be a valid list [type=list_type, input_value='[{"content": "Search for...', input_type=str] ``` The error originates in pydantic validation inside `StructuredTool._parse_input()` — the `todos` field expects `list[Todo]` but receives a `str` containing valid JSON. **Our workaround:** We monkey-patch `TodoListMiddleware.__init__` to wrap the `write_todos` tool's `invoke`/`ainvoke` with input coercion. The wrapper intercepts both flat dict and ToolCall-format input, parses `todos` from string to list via `json.loads()`, and passes the coerced input to the original method. If parsing fails, it passes through unchanged so pydantic gives its normal error. Key implementation detail: `StructuredTool` is a pydantic `BaseModel`, so replacing `invoke`/`ainvoke` requires `object.__setattr__()` to bypass pydantic's `__setattr__`. ```python # Simplified version of our patch def _coerce_todos_input(tool_input): # Handle ToolCall format: {"args": {"todos": "[...]"}, ...} args = tool_input.get("args") if isinstance(args, dict) and "todos" in args: todos = args["todos"] if isinstance(todos, str): parsed = json.loads(todos) if isinstance(parsed, list): return {**tool_input, "args": {**args, "todos": parsed}} # Handle flat format: {"todos": "[...]"} todos = tool_input.get("todos") if isinstance(todos, str): parsed = json.loads(todos) if isinstance(parsed, list): return {**tool_input, "todos": parsed} return tool_input ``` **Suggested upstream fix:** Add coercion logic to the `write_todos` tool definition itself (or its `args_schema`) so that string-encoded lists are accepted. This would be a one-line validator on the `Todo` list field. Happy to contribute a PR if that approach works for you. **Environment:** - deepagents 0.3.12 - langchain-core 0.3.x - Model: Gemini 3 Flash Preview via Vertex AI - Python 3.13
Author
Owner

@AndrewRainforest commented on GitHub (Feb 11, 2026):

Facing the same problem when gemini-2.5-flash generates print(default_api.write_todos(todos=[....

@AndrewRainforest commented on GitHub (Feb 11, 2026): Facing the same problem when gemini-2.5-flash generates `print(default_api.write_todos(todos=[...`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/deepagents#45