{"record":{"id":"abe723f400061721","repo":"zed-industries/zed","slug":"dcr-failed-with-status","errorCode":null,"errorMessage":"DCR failed with status {}: {}","messagePattern":"DCR failed with status (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/context_server/src/oauth.rs","lineNumber":948,"sourceCode":") -> Result<OAuthClientRegistration> {\n    validate_oauth_url(registration_endpoint)?;\n\n    let body = dcr_registration_body(redirect_uri, server_grant_types);\n    let body_bytes = serde_json::to_vec(&body)?;\n\n    let request = Request::builder()\n        .method(http_client::http::Method::POST)\n        .uri(registration_endpoint.as_str())\n        .header(\"Content-Type\", \"application/json\")\n        .header(\"Accept\", \"application/json\")\n        .body(AsyncBody::from(body_bytes))?;\n\n    let mut response = http_client.send(request).await?;\n\n    if !response.status().is_success() {\n        let mut error_body = String::new();\n        response.body_mut().read_to_string(&mut error_body).await?;\n        bail!(\n            \"DCR failed with status {}: {}\",\n            response.status(),\n            error_body\n        );\n    }\n\n    let mut response_body = String::new();\n    response\n        .body_mut()\n        .read_to_string(&mut response_body)\n        .await?;\n\n    let dcr_response: DcrResponse =\n        serde_json::from_str(&response_body).context(\"failed to parse DCR response\")?;\n\n    Ok(OAuthClientRegistration {\n        client_id: dcr_response.client_id,\n        client_secret: dcr_response.client_secret,","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/context_server/src/oauth.rs#L930-L966","documentation":"perform_dcr() POSTs an RFC 7591 registration document to the auth server's registration_endpoint, and the server answered with a non-2xx status. The message includes the HTTP status code and the raw error body, which usually contains the RFC 7591 or provider-specific error details (invalid_redirect_uri, invalid_client_metadata, etc.). Note the endpoint URL already passed validate_oauth_url, so this is a server-side rejection, not a client-side URL guard.","triggerScenarios":"POST to registration_endpoint returns e.g. 400 with {\"error\":\"invalid_redirect_uri\"} because the loopback redirect URI (with its ephemeral port, per the doc comment on resolve_client_registration) is not allowlisted; 401/403 when DCR requires an initial token or is rate-limited; 405 when the endpoint does not accept POST.","commonSituations":"Auth server restricts redirect URIs to pre-registered patterns and rejects http://localhost:PORT; DCR protected by a registration access token that headless clients don't have; provider disabled open DCR after abuse (returns 403); proxy strips the JSON Content-Type and the server answers 415.","solutions":["Read the status and body in the message: 400 invalid_redirect_uri means allowlist loopback redirects (http://127.0.0.1:* / http://localhost:*) in the server's client-registration policy","Enable anonymous or token-authenticated DCR for this client on the authorization server","Ensure the endpoint speaks RFC 7591 (POST + application/json accepted) and no intermediary mangles the request","If DCR cannot be relaxed, switch the server to CIMD (client_id_metadata_document_supported) so registration is not needed"],"exampleFix":"# before (server policy)\nallowed_redirect_uris: [\"https://app.example.com/callback\"]\n\n# after\nallowed_redirect_uris: [\"https://app.example.com/callback\", \"http://localhost:*\", \"http://127.0.0.1:*\"]","handlingStrategy":"try-catch","validationCode":"// client-side preflight: probe the registration endpoint cheaply before the full flow\nasync fn dcr_endpoint_ready(http: &Arc<dyn HttpClient>, endpoint: &Url) -> bool {\n    let req = Request::builder().method(Method::OPTIONS).uri(endpoint.as_str()).body(AsyncBody::empty());\n    matches!(http.send(req).await.map(|r| r.status().as_u16()), Ok(200 | 204 | 405))\n}","typeGuard":null,"tryCatchPattern":"match perform_dcr(&client, &registration_endpoint, &redirect_uri, grants).await {\n    Err(err) if err.to_string().contains(\"DCR failed\") => {\n        let msg = err.to_string();\n        if msg.contains(\"400\") && msg.contains(\"redirect\") {\n            show_fix(\"allowlist loopback redirect URIs (http://127.0.0.1:*) on the auth server\");\n        } else if msg.contains(\"401\") || msg.contains(\"403\") {\n            show_fix(\"DCR is gated (token/policy) on the auth server\");\n        }\n        Err(err)\n    }\n    other => other,\n}","preventionTips":["Allowlist http://localhost:* and http://127.0.0.1:* redirect patterns for DCR-created clients","Keep DCR open (or issue registration tokens) for the clients that need it","Return RFC 7591 JSON errors from the registration endpoint so failures are diagnosable"],"tags":["oauth","mcp","dcr","http-status","client-registration","rfc-7591"],"backgroundTag":"oauth-dynamic-registration-failed","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}