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

  1. If using a custom base_url for Gemini, verify it points at a Generative Language-compatible API, not a web UI.
  2. Retry once — truncated/transient malformed bodies are occasionally one-off network faults.
  3. Inspect the captured response (on GeminiAPIError.response) to see what was actually returned (often an HTML error page naming the culprit).
  4. 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

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

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/781fb9e75d79d51b. Report an issue: GitHub.