hermes - ✅(Solved) Fix Fix: Missing `name` field in tool messages causes Gemini API HTTP 400 [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#16478Fetched 2026-04-28 06:53:04
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
0
Author
Timeline (top)
labeled ×4cross-referenced ×2commented ×1

Error Message

  1. Gemini API returns: Error code: 400 - Request contains an invalid argument

Error Logs

⚠️ Non-retryable error (HTTP 400) — trying fallback... ❌ Non-retryable error (HTTP 400): Error code: 400 - [{'error': {'code': 400, 'message': 'Request contains an invalid argument.', 'status': 'INVALID_ARGUMENT'}}] 7. Line ~10896: Invalid tool name error recovery 8. Line ~10988: Invalid JSON arguments error recovery 9. Line ~11479: Exception error handler

Root Cause

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.

Fix Action

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.

Workaround

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

PR fix notes

PR #16482: fix(agent): include name field on every role:tool message for Gemini compatibility

Description (problem / solution / changelog)

What does this PR do?

Gemini's OpenAI-compatibility endpoint strictly requires the `name` field on `role: tool` messages — it returns HTTP 400 ("Request contains an invalid argument") when the function name is missing. OpenAI/Anthropic/ollama tolerate the absence, so the gap stays invisible until the conversation accumulates a tool-result turn and the user routes it through Gemini (direct API or via ollama-cloud proxy).

This PR adds a `_get_tool_call_name_static()` helper alongside the existing `_get_tool_call_id_static()`, and populates `name` at every site in `run_agent.py` that constructs a `role: tool` message:

#SiteWhat it produces
1Pre-call sanitizer stub (4711)"[Result unavailable]" filler for missing tool result
2tool-call args repair marker (8055)corruption marker injection
3Parallel exec — interrupt skip (8377)per-tool cancellation skip
4Parallel exec — tool result (8642)normal tool result
5Parallel exec — cross-call interrupt (8679)post-cancel skip for remaining calls
6Sequential exec — tool result (9006)normal tool result
7Sequential exec — cross-call interrupt (9032)post-cancel skip for remaining calls
8Invalid tool-name recovery (11837)"Tool 'X' does not exist"
9Invalid JSON-args recovery (11928)"Invalid JSON arguments"
10Exception fallback (12413)synthetic error result for orphaned tool_calls

Each call site was already in scope of the function name (`function_name`, `skipped_name`, `name`, or a dict tool_call), so the change is local — no new lookups, no behavior change for providers that already worked.

Related Issue

Fixes #16478

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • `run_agent.py` — new `_get_tool_call_name_static()` helper + `name` field added to all 10 `role: tool` construction sites.
  • `tests/run_agent/test_agent_guardrails.py` — 6 new helper tests (`TestGetToolCallNameStatic`).
  • `tests/run_agent/test_tool_call_args_sanitizer.py` — updated `test_marker_message_inserted_when_missing` to assert the new `name` field.

How to Test

  1. Configure a Gemini model as primary or fallback (e.g. `gemini-2.5-flash` via direct Gemini API, or `gemini-3-flash-preview` via ollama-cloud).
  2. Send a message that triggers a tool call (e.g. ask it to read a URL via `web_extract`).
  3. Continue the conversation through several tool rounds.
  4. Before this PR: `Error code: 400 - Request contains an invalid argument`. After: tool turns succeed.

Automated: ```bash pytest tests/run_agent/test_agent_guardrails.py tests/run_agent/test_tool_call_args_sanitizer.py

42 passed

``` `tests/run_agent/` overall: 555 passed (one pre-existing flaky thread-timing failure unrelated to this change).

Notes

The helper docstring spells out that `name` is best-effort: when the upstream tool_call genuinely has no `function.name` (corrupt history, dict without function key), the helper returns `""` and OpenAI/Anthropic/ollama still accept it. Gemini will only reject when both the field is empty and the conversation is being replayed there — which is the same failure mode as today.

A future cleanup could push `name` injection into a single `_make_tool_message()` constructor, but the spread-out call sites were left alone here to keep the diff a one-line addition per site.

Changed files

  • run_agent.py (modified, +27/-0)
  • tests/run_agent/test_agent_guardrails.py (modified, +31/-0)
  • tests/run_agent/test_tool_call_args_sanitizer.py (modified, +1/-0)

PR #16583: fix(agent): add missing name field to tool messages for Gemini compatibility

Description (problem / solution / changelog)

Fixes NousResearch/hermes-agent#16478

Problem

All tool result messages (role: "tool") were missing the required name field. While OpenAI's API tolerates this, Gemini and other strict OpenAI-compatible providers return HTTP 400 when name is absent.

Root Cause

The tool message dicts in run_agent.py only included role, content, and tool_call_id, but omitted the name field that identifies which function the tool response belongs to.

Fix

Added "name": <function_name> to all 9 tool message construction sites in run_agent.py:

  1. Context sanitizer stub (line ~4716) — pre-call gap-filler
  2. Argument repair insertion (line ~8248) — corrupted JSON repair
  3. Parallel pre-flight interrupt (line ~8568) — user interrupt during parallel
  4. Parallel execution result (line ~8834) — main parallel tool result
  5. Sequential interrupt mid-tool (line ~8872) — user interrupt during sequential
  6. Sequential execution result (line ~9199) — main sequential tool result
  7. Sequential interrupt after-tool (line ~9227) — user interrupt after partial completion
  8. Invalid tool name error (line ~12059) — unknown tool self-correction
  9. Invalid JSON args error (line ~12151) — malformed arguments self-correction

Testing

  • All 10 role: "tool" messages now include the name field
  • Syntax verified with ast.parse() on the full file
  • No functional changes to tool execution logic — only message formatting

Changed files

  • run_agent.py (modified, +12/-2)

Code Example

⚠️ 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'}}]

---

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

---

+    @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 ""
RAW_BUFFERClick to expand / collapse

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.

extent analysis

TL;DR

Add the name field to all role: "tool" messages in run_agent.py to fix the Gemini API compatibility issue.

Guidance

  • Identify the 9 locations in run_agent.py where role: "tool" messages are constructed and add the name field to each.
  • Implement the _get_tool_call_name_static() helper method to extract function names from tool calls.
  • Verify that the name field is correctly added to tool messages by checking the request dump.
  • Test the fix with Gemini models to ensure the HTTP 400 error is resolved.

Example

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 ""

Notes

The fix only applies to Gemini models, and other models like OpenAI and Anthropic are unaffected. The workaround is to apply the patch, as there is no temporary solution once the conversation accumulates tool messages without the name field.

Recommendation

Apply the workaround by adding the name field to all role: "tool" messages in run_agent.py, as this is the only way to resolve the Gemini API compatibility issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix Fix: Missing `name` field in tool messages causes Gemini API HTTP 400 [2 pull requests, 1 comments, 2 participants]