{"record":{"id":"7ee89715cab60ef1","repo":"BerriAI/litellm","slug":"this-server-requires-pkce-send-code-challenge-wit","errorCode":null,"errorMessage":"This server requires PKCE: send code_challenge with code_challenge_method=S256 on the authorization request","messagePattern":"This server requires PKCE: send code_challenge with code_challenge_method=S256 on the authorization request","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py","lineNumber":705,"sourceCode":"    upstream holds each client's own registration, so the authorize and token relays pass the\n    client's ``client_id`` and ``redirect_uri`` through verbatim and the authorization code\n    returns directly to the client's redirect URI without transiting the gateway. Gateway-side\n    redirect trust and the ``/callback`` state relay therefore only apply to the short-circuit\n    arm, where the upstream only knows the gateway's own callback.\"\"\"\n    return mcp_server.is_dcr_bridge and bool(mcp_server.registration_url) and not mcp_server.client_id\n\n\ndef _require_s256_pkce(\n    code_challenge: str | None,\n    code_challenge_method: str | None,\n) -> tuple[str, str]:\n    \"\"\"DCR-bridge servers serve unauthenticated public OAuth clients, so the PKCE downgrade\n    paths (no challenge, or a non-S256 method; RFC 7636 defaults a missing method to ``plain``)\n    are rejected at the gateway instead of relying on upstream enforcement. Returns the\n    validated pair so callers get non-optional values.\"\"\"\n    if code_challenge and code_challenge_method == \"S256\":\n        return code_challenge, code_challenge_method\n    raise HTTPException(\n        status_code=400,\n        detail=(\n            \"This server requires PKCE: send code_challenge with \"\n            \"code_challenge_method=S256 on the authorization request\"\n        ),\n    )\n\n\ndef _redirect_to_upstream_authorize(\n    *,\n    mcp_server: MCPServer,\n    client_id: str,\n    redirect_uri: str,\n    state: str,\n    code_challenge: str,\n    code_challenge_method: str,\n    response_type: str | None,\n    scope: str | None,","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py#L687-L723","documentation":"Raised by _require_s256_pkce when an authorization request against a PKCE-mandated server arrives without a code_challenge or with a method other than S256; the gateway refuses to start the flow because the server row requires proof-key exchange.","triggerScenarios":"Thrown at litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py:705 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Include code_challenge with code_challenge_method=S256 on the authorization request."],"exampleFix":"code_challenge=<s256>&code_challenge_method=S256","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}