{"record":{"id":"9c100d7d217378d1","repo":"BerriAI/litellm","slug":"refusing-to-send-the-server-configured-credential","errorCode":null,"errorMessage":"Refusing to send the server-configured {credential_name} to the caller-supplied api_base '{caller_api_base}'. Pass an explicit api_key when overriding api_base for this search provider.","messagePattern":"Refusing to send the server-configured (.+?) to the caller-supplied api_base '(.+?)'\\. Pass an explicit api_key when overriding api_base for this search provider\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/base_llm/search/transformation.py","lineNumber":138,"sourceCode":"        self,\n        caller_api_base: str | None,\n        default_api_base: str | None,\n        base_env_var: str | None,\n        credential_name: str,\n    ) -> None:\n        \"\"\"\n        Block sending a server-managed credential to a caller-chosen host.\n\n        A caller-supplied api_base is honored when constructing the request URL, so\n        falling back to a server-configured secret while the caller controls the host\n        leaks that secret. The provider default and the operator's own api_base\n        override are the only trusted destinations for a server-managed credential.\n        \"\"\"\n        if not caller_api_base:\n            return\n        if _is_trusted_search_api_base(caller_api_base, default_api_base, base_env_var):\n            return\n        raise ValueError(\n            f\"Refusing to send the server-configured {credential_name} to the \"\n            f\"caller-supplied api_base '{caller_api_base}'. Pass an explicit api_key \"\n            f\"when overriding api_base for this search provider.\"\n        )\n\n    def resolve_server_api_key(\n        self,\n        *,\n        caller_api_key: str | None,\n        caller_api_base: str | None,\n        key_env_vars: tuple[str, ...],\n        base_env_var: str | None,\n        default_api_base: str | None,\n    ) -> str | None:\n        \"\"\"\n        Resolve a single-secret search API key, falling back to a server-managed\n        secret only when the request targets a trusted host.\n","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/base_llm/search/transformation.py#L120-L156","documentation":"A credential-exfiltration guard in BaseSearchConfig (litellm/llms/base_llm/search/transformation.py). When a caller overrides api_base for a search provider, LiteLLM refuses to attach a server-managed credential (one configured on the litellm server, e.g. via env/hooks) to the request, unless the caller-supplied base is 'trusted' — i.e. it matches the provider default or the operator's own configured base (checked by _is_trusted_search_api_base). Otherwise it raises ValueError, because honoring a caller-chosen host with a server secret would let any API consumer harvest that secret by pointing api_base at their own server.","triggerScenarios":"Calling litellm search/web-search with both (a) an api_base that differs from the provider default/operator-configured base and (b) relying on a server-configured key (no explicit api_key in the call). E.g. litellm.search(..., api_base=\"https://my-proxy.example/search\", key_env_vars resolved server-side).","commonSituations":"Routing a hosted search provider (Google PSE, Exa, etc.) through an internal gateway by overriding api_base while expecting the litellm server's key to still be used; multi-tenant proxy setups where tenants pass their own api_base; moving a provider to a mirror URL without re-configuring the key.","solutions":["Pass an explicit api_key together with the custom api_base: litellm.search(..., api_base=\"https://my-host\", api_key=\"sk-...\") — the guard only blocks server-managed secrets.","Or make the custom base trusted: configure it as the operator-side api_base override (provider settings / base_env_var) rather than passing it per-request.","Or drop the api_base override and use the provider's default endpoint with the server-configured credential.","If you operate the proxy, review whether tenants should be allowed to redirect search traffic at all; this error is the intended security behavior, not a bug."],"exampleFix":"# before\nlitellm.search(  # server-configured key + caller api_base -> ValueError\n    provider=\"exa\", query=\"litellm\",\n    api_base=\"https://search.internal.example\",\n)\n\n# after\nlitellm.search(\n    provider=\"exa\", query=\"litellm\",\n    api_base=\"https://search.internal.example\",\n    api_key=os.environ[\"MY_EXA_KEY\"],  # explicit key is allowed\n)","handlingStrategy":"validation","validationCode":"# caller-side: never rely on a server-managed key with a custom api_base\nassert not (api_base and not api_key), \"explicit api_key required when overriding api_base\"","typeGuard":"def search_call_is_safe(api_base: str | None, api_key: str | None) -> bool:\n    # safe only when: default base, or explicit key supplied with custom base\n    return api_base is None or api_key is not None","tryCatchPattern":"try:\n    litellm.search(provider=\"exa\", query=q, api_base=base)\nexcept ValueError as e:\n    if \"Refusing to send the server-configured\" in str(e):\n        raise PermissionError(\"pass an explicit api_key for custom search api_base\") from None\n    raise","preventionTips":["Treat api_base overrides as untrusted combinations: always pair them with an explicit api_key.","Configure operator-level api_base overrides in litellm config instead of per-request when the server credential must be used.","Never attempt to bypass this guard; it exists to prevent secret exfiltration by API callers."],"tags":["security","credential-leak","search-api","api-base","value-error","litellm"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}