BerriAI/litellm · error · HTTPException
challenge.body if challenge.body is not None else error.summ
Error message
challenge.body if challenge.body is not None else error.summary
What it means
raise_public maps an outbound-credential resolution failure tagged unauthorized onto the proxy's public contract: HTTP 401 whose detail is the challenge body (or the CredError summary) and whose WWW-Authenticate header relays the upstream challenge. It fires when the proxy must authenticate to an upstream MCP server (per-user OAuth, delegated auth, or token exchange) and the stored credential was rejected by the upstream IdP or server.
Source
Thrown at litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py:273
return ClientSecretAuth(client_secret=SecretStr(server.client_secret))
return None
def _id_jag_subject_token_type(server: MCPServer) -> str:
"""ID-JAG asserts the user's id_token, so the token-exchange access_token default maps to id_token;
an explicitly configured value (e.g. a SAML2 assertion type) is honored verbatim."""
configured: Final = server.subject_token_type
if configured and configured != _TOKEN_EXCHANGE_SUBJECT_TOKEN_DEFAULT:
return configured
return _ID_JAG_SUBJECT_TOKEN_DEFAULT
def raise_public(error: CredError) -> NoReturn:
"""Map a resolver CredError onto the proxy's public HTTP contract. The one edge that raises."""
match error.tag:
case "unauthorized":
challenge: Final = error.unauthorized
raise HTTPException(
status_code=401,
detail=challenge.body if challenge.body is not None else error.summary,
headers=({"WWW-Authenticate": challenge.www_authenticate} if challenge.www_authenticate else None),
)
case "misconfigured":
raise HTTPException(status_code=500, detail=error.summary)
case "upstream_unavailable":
raise HTTPException(status_code=503, detail=error.summary)
case "unsupported_mode":
raise HTTPException(status_code=500, detail=error.summary)
case "precondition_required":
raise HTTPException(status_code=412, detail=error.summary)
case "not_implemented":
raise HTTPException(status_code=501, detail=error.summary)
assert_never(error.tag)
def oauth_protected_resource_path(root_path: str, server: MCPServer) -> str:View on GitHub (pinned to 77b7c6c40c)
Solutions
- Re-authenticate: follow the WWW-Authenticate challenge on the 401 (it points at the server's RFC 9728 protected-resource metadata) and run the OAuth flow to mint fresh credentials.
- If re-auth keeps failing, delete the stored credential (per-user/BYOK OAuth UI or DB) and re-consent from scratch.
- For token-exchange servers, verify the gateway's client credentials and audience/scope - an IdP rejecting the gateway itself is a different code path (500), but a mis-scoped user token shows up here too.
Defensive patterns
Strategy: try-catch
Try / catch
resp = await client.post(f"{proxy}/mcp/tool-call", json=payload, headers=headers)
if resp.status_code == 401:
challenge = resp.headers.get("www-authenticate", "")
if "resource_metadata=" in challenge:
await reauthorize_upstream(challenge) # run the advertised OAuth flow, store fresh creds
resp = await client.post(f"{proxy}/mcp/tool-call", json=payload, headers=headers)
resp.raise_for_status() Prevention
- Treat 401 + WWW-Authenticate from the proxy as a re-auth signal, not a hard failure.
- Refresh per-user OAuth credentials proactively on a schedule shorter than the IdP token lifetime.
- Alert when re-auth loops repeat - usually revoked consent or a rotated app secret.
When it happens
Trigger: Calling an MCP tool or the tools/list REST facade for a server with delegated/per-user upstream OAuth where the user's or key's tokens are expired, revoked, or invalid; the token endpoint or upstream returned 401/invalid_grant during resolve_credentials; the challenge is relayed verbatim so standards-compliant MCP clients can start the upstream OAuth flow.
Common situations: Per-user OAuth tokens gone stale (IdP rotation window passed, user inactive); user or admin revoked consent for the OAuth app; refresh-token rotation invalidated the stored token; upstream API keys rotated without updating the stored credential.
Related errors
- error.summary
- IdP rejected the subject token (HTTP {status_code})
- Authentication failed. Check your BitBucket access token and
- OAuth M2M token request failed: {response.text}
- GigaChat credentials not provided. Set GIGACHAT_CREDENTIALS
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/a1f4e4587da29938.
Report an issue: GitHub.