BerriAI/litellm · error · HTTPException
token_endpoint_auth_method=client_secret_basic requires both
Error message
token_endpoint_auth_method=client_secret_basic requires both client_id and client_secret
What it means
Pre-flight check for the client_secret_basic token-endpoint auth method: basic auth is mathematically impossible without both halves of the credential pair, so the request is rejected 400 before the upstream exchange. Applies to the caller-supplied client case; gateway-stored clients use their own registered method.
Source
Thrown at litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py:918
# recovered from a sealed code) must authenticate the way its own registration was granted,
# not the way the server row is configured; callers that carry no method keep the row's method
# as before.
resolved_client_id: Final = mcp_server.client_id if mcp_server.client_id else client_id
resolved_client_secret: Final = mcp_server.client_secret if mcp_server.client_id else client_secret
resolved_auth_method: Final = (
mcp_server.token_endpoint_auth_method
if mcp_server.client_id
else (client_token_endpoint_auth_method or mcp_server.token_endpoint_auth_method)
)
try:
token_request: Final = build_upstream_oauth2_token_request(
mcp_server,
auth_method=resolved_auth_method,
client_id=resolved_client_id,
client_secret=resolved_client_secret,
)
except TokenEndpointAuthConfigError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
bridge_identity: _BridgeAuthorizationCode | None = None
bridge_mint_ready: _BridgeMintReady | None = None
bridge_upstream_refresh: SecretStr | None = None
bridge_upstream_scope: str | None = None
refresh_request_scope: str | None = None
is_bridge: Final = mcp_server.is_oauth_delegate and mcp_server.is_dcr_bridge
if grant_type == "refresh_token":
# Phase 1 for a bridge refresh: open the client's refresh envelope, re-validate the sealed
# identity, and unwrap the real upstream refresh token BEFORE building token_data, so the exchange
# sends the upstream token and never the envelope. A failure returns without touching the upstream.
if is_bridge:
prepared_refresh: Final = await _prepare_bridge_refresh(mcp_server, refresh_token)
if not isinstance(prepared_refresh, _BridgeRefreshReady):
return _bridge_mint_error_response(prepared_refresh)
bridge_mint_ready = prepared_refresh.ready
bridge_upstream_refresh = prepared_refresh.upstream_refresh_tokenView on GitHub (pinned to 77b7c6c40c)
Solutions
- Provide both client_id and client_secret for client_secret_basic authentication.
Example fix
Send Authorization: Basic base64(client_id:client_secret).
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py:918 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/b9a2ba6dc828105c.
Report an issue: GitHub.