BerriAI/litellm · error · ValueError
advisor tool definition sets 'api_base' without 'api_key'. A
Error message
advisor tool definition sets 'api_base' without 'api_key'. A caller-supplied api_base is only honored alongside a caller-supplied api_key, so the proxy's own credentials are never sent to a caller-chosen destination.
What it means
Security guard in the advisor credential resolver. A caller may point the advisor at a custom api_base only when the caller also supplies the api_key for that destination. Supplying api_base without api_key is rejected so the proxy never attaches its own credentials to a caller-chosen URL (credential-exfiltration / SSRF-to-credential-theft protection). Only active when client-side advisor credentials are allowed.
Source
Thrown at litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py:214
the caller-chosen ``api_base``. A caller-supplied ``api_base`` is also
required to be https with TLS verification on, and SSRF-validated so it
can't target a private/internal/cloud-metadata address, mirroring
``proxy.auth.auth_utils.check_complete_credentials``. https with TLS
verification is required because ``validate_url`` only rewrites the
connection to a DNS-pinned IP for http, or for https with
``litellm.ssl_verify`` disabled; otherwise it returns the URL unchanged
and relies on certificate validation to block DNS rebinding, so this
closes the same gap without threading the pinned URL through the whole
``anthropic_messages()`` call chain.
"""
if not _allow_client_side_advisor_credentials():
return None, None
api_key: Final[str | None] = advisor_tool.get("api_key")
api_base: Final[str | None] = advisor_tool.get("api_base")
if api_base is None:
return api_key, None
if not api_key:
raise ValueError(
"advisor tool definition sets 'api_base' without 'api_key'. A "
"caller-supplied api_base is only honored alongside a "
"caller-supplied api_key, so the proxy's own credentials are "
"never sent to a caller-chosen destination."
)
if not api_base.startswith("https://"):
raise ValueError(f"advisor tool definition sets 'api_base'={api_base!r}, which must use the https scheme.")
if getattr(litellm, "ssl_verify", True) is False:
raise ValueError(
"advisor tool definition sets 'api_base' but the proxy has TLS verification "
"disabled (litellm.ssl_verify=False), so a caller-supplied api_base can't be "
"safely validated against DNS rebinding."
)
if getattr(litellm, "user_url_validation", True):
validate_url(api_base)
return api_key, api_base
View on GitHub (pinned to 6c2dcb801b)
Solutions
- Supply both fields together: api_key and api_base on the advisor tool definition.
- If the destination should use the gateway's configured credentials, remove 'api_base' and configure that model in the proxy's model_list instead.
- If you truly need gateway credentials at a custom base, register that model+credentials server-side rather than client-side.
Example fix
# before
tools = [{"type": "advisor", "model": "claude-haiku-4-5", "api_base": "https://internal.example.com"}]
# after
tools = [{
"type": "advisor",
"model": "claude-haiku-4-5",
"api_base": "https://internal.example.com",
"api_key": "sk-internal-key",
}] Defensive patterns
Strategy: validation
Validate before calling
def validate_advisor_credentials(advisor_tool: dict) -> None:
api_base = advisor_tool.get("api_base")
api_key = advisor_tool.get("api_key")
if api_base is not None and not api_key:
raise ValueError("advisor api_base requires a matching caller-supplied api_key") Type guard
def advisor_credentials_are_paired(tool: dict) -> bool:
api_base = tool.get("api_base")
api_key = tool.get("api_key")
return api_base is None or bool(api_key) Try / catch
try:
resp = litellm.anthropic_messages(tools=tools, ...)
except ValueError as e:
if "api_base" in str(e) and "api_key" in str(e):
log_security_event("advisor_credential_mismatch", str(e))
return http_error(400, "advisor api_base must be paired with api_key")
raise Prevention
- Treat api_key and api_base as an atomic pair in advisor tool config.
- Prefer server-side model registration over caller-supplied bases when using gateway credentials.
- Never expect the proxy's own key to follow a caller-chosen destination.
When it happens
Trigger: Sending an advisor tool with {"type": "advisor", "model": ..., "api_base": "https://my-llm.example.com"} but no 'api_key' key (or api_key=None/""). The resolver sees api_base set, api_key falsy, and raises.
Common situations: Teams pointing the advisor at an internal vLLM endpoint but expecting the proxy's key to be reused; half-finished config where the base was migrated to the tool definition but the key was left in an env var; attempts (accidental or malicious) to make the gateway forward its Anthropic key to an attacker-controlled host.
Related errors
- advisor tool definition sets 'api_base'={api_base!r}, which
- advisor tool definition sets 'api_base' but the proxy has TL
- MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. htt
- Mavvrik FOCUS destination: {label} must be a GCS endpoint (s
- {field_name} cannot be a dot path segment
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/8fbe081f65abe254.
Report an issue: GitHub.