{"record":{"id":"a22b200d598f82d5","repo":"BerriAI/litellm","slug":"error-summary","errorCode":null,"errorMessage":"error.summary","messagePattern":"error\\.summary","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py","lineNumber":279,"sourceCode":"    an explicitly configured value (e.g. a SAML2 assertion type) is honored verbatim.\"\"\"\n    configured: Final = server.subject_token_type\n    if configured and configured != _TOKEN_EXCHANGE_SUBJECT_TOKEN_DEFAULT:\n        return configured\n    return _ID_JAG_SUBJECT_TOKEN_DEFAULT\n\n\ndef raise_public(error: CredError) -> NoReturn:\n    \"\"\"Map a resolver CredError onto the proxy's public HTTP contract. The one edge that raises.\"\"\"\n    match error.tag:\n        case \"unauthorized\":\n            challenge: Final = error.unauthorized\n            raise HTTPException(\n                status_code=401,\n                detail=challenge.body if challenge.body is not None else error.summary,\n                headers=({\"WWW-Authenticate\": challenge.www_authenticate} if challenge.www_authenticate else None),\n            )\n        case \"misconfigured\":\n            raise HTTPException(status_code=500, detail=error.summary)\n        case \"upstream_unavailable\":\n            raise HTTPException(status_code=503, detail=error.summary)\n        case \"unsupported_mode\":\n            raise HTTPException(status_code=500, detail=error.summary)\n        case \"precondition_required\":\n            raise HTTPException(status_code=412, detail=error.summary)\n        case \"not_implemented\":\n            raise HTTPException(status_code=501, detail=error.summary)\n    assert_never(error.tag)\n\n\ndef oauth_protected_resource_path(root_path: str, server: MCPServer) -> str:\n    \"\"\"The server's RFC 9728 Protected Resource Metadata path, the shared anchor of both challenges.\n\n    ``root_path`` is the proxy's ``SERVER_ROOT_PATH``, resolved by the caller (the imperative shell)\n    so this stays a pure function of its inputs; ``\"/\"`` and ``\"\"`` both mean no prefix. The path is\n    relative, so it resolves against the caller's own host (correct even behind a reverse proxy).\n    \"\"\"","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/outbound_credentials/adapter.py#L261-L297","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read error.summary in the 500 body - it names the exact inconsistency in that server's auth configuration.","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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp = await client.post(f\"{proxy}/mcp/tool-call\", json=payload, headers=headers)\nif resp.status_code == 500:\n    summary = resp.json().get(\"detail\") if isinstance(resp.json().get(\"detail\"), str) else resp.text\n    alert_ops(f\"MCP server auth misconfigured: {summary}\")  # never retry; admin must fix config\nresp.raise_for_status()","preventionTips":["Pin MCP server auth blocks to one documented example per litellm version and review them on upgrade.","Run the proxy's config validation at deploy time so misconfigured auth entries fail the pipeline, not production traffic.","Keep error.summary from these 500s in alerts - it names the exact broken field."],"tags":["configuration","http-500","oauth","mcp","credentials"],"backgroundTag":"oauth-misconfiguration","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}