BerriAI/litellm · error · ValueError

When overriding api_base for Gemini agents, you must also su

Error message

When overriding api_base for Gemini agents, you must also supply an explicit api_key. Falling back to GOOGLE_API_KEY / GEMINI_API_KEY env vars with a custom api_base is refused to prevent leaking the shared provider key to arbitrary hosts.

What it means

A deliberate security guard in the Gemini agents transformation: when the caller overrides api_base (e.g. routing through a custom host or proxy), litellm refuses to attach the process-wide GOOGLE_API_KEY/GEMINI_API_KEY and instead raises this ValueError demanding an explicit api_key. Without the guard, an attacker who can set api_base could point the request at their own host and exfiltrate the shared Gemini key via the x-goog-api-key header.

Source

Thrown at litellm/llms/gemini/agents/transformation.py:108

        litellm_params: dict[str, Any],
    ) -> str:
        return f"{self._base_url(api_base)}/agents"

    def validate_environment(
        self,
        headers: dict[str, str],
        litellm_params: dict[str, Any],
    ) -> dict[str, str]:
        headers = dict(headers)
        headers["Content-Type"] = "application/json"
        explicit_api_key: Final = litellm_params.get("api_key")
        # SECURITY: when the caller overrides ``api_base``, refuse to fall back
        # to the process-wide GOOGLE_API_KEY / GEMINI_API_KEY env vars. Otherwise
        # an authenticated proxy user could set ``api_base`` to an attacker-
        # controlled host and have the proxy ship its shared Gemini key in the
        # ``x-goog-api-key`` header.
        if litellm_params.get("api_base") and not explicit_api_key:
            raise ValueError(
                "When overriding api_base for Gemini agents, you must also "
                "supply an explicit api_key. Falling back to GOOGLE_API_KEY / "
                "GEMINI_API_KEY env vars with a custom api_base is refused "
                "to prevent leaking the shared provider key to arbitrary hosts."
            )
        api_key: Final = GeminiModelInfo.get_api_key(explicit_api_key)
        if not api_key:
            raise ValueError("Google API key is required. Set GOOGLE_API_KEY or GEMINI_API_KEY, or pass api_key.")
        headers["x-goog-api-key"] = api_key
        return headers

    def _raise_for_status(self, raw_response: httpx.Response) -> None:
        if not (200 <= raw_response.status_code < 300):
            raise GeminiError(
                message=raw_response.text,
                status_code=raw_response.status_code,
                headers=dict(raw_response.headers),
            )

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Pass an explicit api_key together with the custom api_base: api_key='...' alongside api_base='...'.
  2. If the proxy injects its own upstream credentials, pass a placeholder/dedicated key scoped to that hop.
  3. Remove the api_base override to use the official Google endpoint, where the env-var fallback is allowed.

Example fix

# before
litellm.completion(model="gemini/gemini-2.0-flash", messages=m, api_base="https://llm-gw.internal")  # ValueError

# after
litellm.completion(model="gemini/gemini-2.0-flash", messages=m, api_base="https://llm-gw.internal", api_key=os.environ["GEMINI_GATEWAY_KEY"])
Defensive patterns

Strategy: validation

Validate before calling

def gemini_call_params(api_base: str | None, api_key: str | None) -> dict:
    if api_base and not api_key:
        raise ValueError(
            "Custom api_base for Gemini agents requires an explicit api_key "
            "(env-var fallback is disabled for security)"
        )
    return {k: v for k, v in {"api_base": api_base, "api_key": api_key}.items() if v}

Type guard

def is_safe_gemini_base_override(litellm_params: dict) -> bool:
    return not litellm_params.get("api_base") or bool(litellm_params.get("api_key"))

Try / catch

try:
    litellm.completion(model="gemini/gemini-2.0-flash", messages=m, api_base=gw_url)
except ValueError as e:
    if "must also supply an explicit api_key" in str(e):
        params["api_key"] = gateway_key  # dedicated key for the overridden hop
        litellm.completion(model="gemini/gemini-2.0-flash", messages=m, **params)
    else:
        raise

Prevention

When it happens

Trigger: litellm_params contains api_base (custom proxy/gateway URL) but no api_key, e.g. completion(model='gemini/...', ..., api_base='https://my-proxy.example.com') while relying on env-var GEMINI_API_KEY.

Common situations: Routing Gemini agent traffic through an internal LLM gateway for logging/rate limiting; corporate proxy setups; testing against a local mock server. All previously worked by silently sending the env key to the overridden host, and now fail closed by design.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/3bd86563583e1d24. Report an issue: GitHub.