BerriAI/litellm · error · HTTPException

error.summary

Error message

error.summary

What it means

raise_public maps a CredError tagged misconfigured to HTTP 500 with error.summary: the proxy's own credential configuration for that MCP server is internally inconsistent, so the request can never succeed until an admin fixes the server definition. It is deliberately not caller-fixable and not retryable.

Source

Thrown at litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py:279

    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:
    """The server's RFC 9728 Protected Resource Metadata path, the shared anchor of both challenges.

    ``root_path`` is the proxy's ``SERVER_ROOT_PATH``, resolved by the caller (the imperative shell)
    so this stays a pure function of its inputs; ``"/"`` and ``""`` both mean no prefix. The path is
    relative, so it resolves against the caller's own host (correct even behind a reverse proxy).
    """

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Read error.summary in the 500 body - it names the exact inconsistency in that server's auth configuration.
  2. Fix the mcp_servers entry in config.yaml (or the DB/UI) to match the current litellm MCP auth schema, then restart or hot-reload the proxy.
  3. Validate the config with the proxy's config-check tooling or startup logs before deploying; keep auth fields for one server from a single documented example.
Defensive patterns

Strategy: try-catch

Try / catch

resp = await client.post(f"{proxy}/mcp/tool-call", json=payload, headers=headers)
if resp.status_code == 500:
    summary = resp.json().get("detail") if isinstance(resp.json().get("detail"), str) else resp.text
    alert_ops(f"MCP server auth misconfigured: {summary}")  # never retry; admin must fix config
resp.raise_for_status()

Prevention

When it happens

Trigger: An mcp_servers entry with delegated/token-exchange auth whose fields contradict each other (e.g. missing token endpoint, unknown auth mode, unsupported field combination) - resolve_credentials classifies it misconfigured and every tool call or tools/list for that server returns 500 with the summary text.

Common situations: Config drift after a litellm upgrade introduces new required OAuth fields (audience, authentication_mode, token endpoint) that old entries lack; hand-edited config.yaml with mixed old/new auth keys; copying a server block from docs of a different version.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/a22b200d598f82d5. Report an issue: GitHub.