Skip to content

Fix: Missing name field in tool messages causes Gemini API HTTP 400 #16478

@dataofmen

Description

@dataofmen

Bug Description

When using Gemini models (via direct API or ollama-cloud proxy), the agent fails with HTTP 400 "Request contains an invalid argument" errors. This happens because role: "tool" messages are missing the required name field.

The OpenAI API spec and Gemini's OpenAI-compatibility endpoint require the name field in tool response messages. While some providers (OpenAI, ollama) tolerate its absence, Gemini strictly enforces it.

Steps to Reproduce

  1. Configure hermes with a Gemini model as primary or fallback (e.g., gemini-3-flash-preview via ollama-cloud, or gemini-2.5-flash via direct Gemini API)
  2. Send a message that triggers tool use (e.g., a URL for web_extract)
  3. After a few tool call rounds, the accumulated conversation contains tool messages without name
  4. Gemini API returns: Error code: 400 - Request contains an invalid argument

Error Logs

⚠️ Non-retryable error (HTTP 400) — trying fallback...
🔄 Primary model failed — switching to fallback: gemini-2.5-flash via gemini
❌ Non-retryable error (HTTP 400): Error code: 400 - [{'error': {'code': 400, 'message': 'Request contains an invalid argument.', 'status': 'INVALID_ARGUMENT'}}]

Request dump shows tool messages like:

{"role": "tool", "tool_call_id": "call_xxx", "content": "..."}

Missing the required name field that Gemini expects.

Fix

Add the name field to all 9 locations in run_agent.py where role: "tool" messages are constructed. Also add a helper method _get_tool_call_name_static() to extract function names from both dict and object format tool calls.

Affected Locations in run_agent.py

  1. Line ~3751: Stub result for missing tool calls (sanitizer)
  2. Line ~7683: Interrupt skip in concurrent execution
  3. Line ~7951: Tool result in concurrent execution
  4. Line ~7977: Interrupt skip in sequential execution
  5. Line ~8309: Tool result in sequential execution
  6. Line ~8330: Interrupt skip in sequential execution
  7. Line ~10896: Invalid tool name error recovery
  8. Line ~10988: Invalid JSON arguments error recovery
  9. Line ~11479: Exception error handler

Patch

+    @staticmethod
+    def _get_tool_call_name_static(tc) -> str:
+        """Extract function name from a tool_call entry (dict or object)."""
+        if isinstance(tc, dict):
+            return tc.get("function", {}).get("name", "") or ""
+        return getattr(getattr(tc, "function", None), "name", "") or ""

Each tool message now includes "name": <function_name> alongside tool_call_id and content.

Environment

  • hermes-agent: current main
  • Models affected: All Gemini models (direct API + ollama-cloud proxy)
  • Models unaffected: OpenAI, Anthropic, local ollama (tolerate missing name)

Workaround

None — once the conversation accumulates tool messages without name, Gemini will keep rejecting the request until the session is cleared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/agentCore agent loop, run_agent.py, prompt builderprovider/geminiGoogle Gemini (AI Studio, Cloud Code)type/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions