NousResearch/hermes-agent · error · GeminiAPIError
gemini_invalid_json
gemini_invalid_json
Error message
Invalid JSON from Gemini native API: {exc} What it means
A non-streaming call to the Gemini native generateContent endpoint returned HTTP 200 but the body was not valid JSON (response.json() raised ValueError). This is wrapped in GeminiAPIError with code 'gemini_invalid_json' and carries the HTTP status and response, distinguishing a malformed-payload failure from transport or HTTP-status errors.
Source
Thrown at agent/gemini_native_adapter.py:1064
temperature=temperature,
max_tokens=max_tokens,
top_p=top_p,
stop=stop,
thinking_config=thinking_config,
)
model = bare_gemini_model_id(model)
if stream:
return self._stream_completion(model=model, request=request, timeout=timeout)
url = f"{self.base_url}/models/{model}:generateContent"
response = self._http.post(url, json=request, headers=self._headers(), timeout=timeout)
if response.status_code != 200:
raise gemini_http_error(response)
try:
payload = response.json()
except ValueError as exc:
raise GeminiAPIError(
f"Invalid JSON from Gemini native API: {exc}",
code="gemini_invalid_json",
status_code=response.status_code,
response=response,
) from exc
return translate_gemini_response(payload, model=model)
def _stream_completion(self, *, model: str, request: Dict[str, Any], timeout: Any = None) -> Iterator[_GeminiStreamChunk]:
url = f"{self.base_url}/models/{model}:streamGenerateContent?alt=sse"
stream_headers = dict(self._headers())
stream_headers["Accept"] = "text/event-stream"
def _generator() -> Iterator[_GeminiStreamChunk]:
try:
with self._http.stream("POST", url, json=request, headers=stream_headers, timeout=timeout) as response:
if response.status_code != 200:
body_text = read_streaming_error_body(response)
raise gemini_http_error(response, body_text=body_text)View on GitHub (pinned to c896c09c42)
Solutions
- If using a custom base_url for Gemini, verify it points at a Generative Language-compatible API, not a web UI.
- Retry once — truncated/transient malformed bodies are occasionally one-off network faults.
- Inspect the captured response (on GeminiAPIError.response) to see what was actually returned (often an HTML error page naming the culprit).
- Bypass intermediaries (VPN, corporate proxy) to confirm they are not rewriting responses.
Defensive patterns
Strategy: retry
Try / catch
from agent.gemini_native_adapter import GeminiAPIError
try:
result = client.chat.completions.create(model=m, messages=msgs)
except GeminiAPIError as e:
if e.code == 'gemini_invalid_json':
# inspect e.response (often an HTML proxy page); fix base_url or retry once
... Prevention
- Point custom base_url at a real Gemini-compatible API endpoint
- Watch for corporate proxies returning HTML on API paths
- Check e.response content on first occurrence to identify the intermediary
When it happens
Trigger: POST {base_url}/models/{model}:generateContent returns 200 with a non-JSON body — e.g. an HTML error page from a proxy, a truncated body, or an intermediary (corporate proxy, custom base_url endpoint) that mangles responses.
Common situations: Custom base_url pointing at a gateway/relay that returns HTML on some paths; Captcha/auth-wall pages from intercepted networks; transient truncated responses; wrong endpoint configuration routing to a web UI instead of the API.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed proxy environment variable {key}={value!r}. Fix or
- Cannot resolve ${installScriptName()}: no SOURCE_REPO_ROOT a
- ${isPosix ? 'install.sh --manifest' : 'install.ps1 -Manifest
- Reached the gateway over HTTP, but the live WebSocket (/api/
- Could not fetch image: ${response.status}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/781fb9e75d79d51b.
Report an issue: GitHub.