zed-industries/zed · error

DCR failed with status {}: {}

Error message

DCR failed with status {}: {}

What it means

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.

Source

Thrown at crates/context_server/src/oauth.rs:948

) -> Result<OAuthClientRegistration> {
    validate_oauth_url(registration_endpoint)?;

    let body = dcr_registration_body(redirect_uri, server_grant_types);
    let body_bytes = serde_json::to_vec(&body)?;

    let request = Request::builder()
        .method(http_client::http::Method::POST)
        .uri(registration_endpoint.as_str())
        .header("Content-Type", "application/json")
        .header("Accept", "application/json")
        .body(AsyncBody::from(body_bytes))?;

    let mut response = http_client.send(request).await?;

    if !response.status().is_success() {
        let mut error_body = String::new();
        response.body_mut().read_to_string(&mut error_body).await?;
        bail!(
            "DCR failed with status {}: {}",
            response.status(),
            error_body
        );
    }

    let mut response_body = String::new();
    response
        .body_mut()
        .read_to_string(&mut response_body)
        .await?;

    let dcr_response: DcrResponse =
        serde_json::from_str(&response_body).context("failed to parse DCR response")?;

    Ok(OAuthClientRegistration {
        client_id: dcr_response.client_id,
        client_secret: dcr_response.client_secret,

View on GitHub (pinned to f4178619ac)

Solutions

  1. 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
  2. Enable anonymous or token-authenticated DCR for this client on the authorization server
  3. Ensure the endpoint speaks RFC 7591 (POST + application/json accepted) and no intermediary mangles the request
  4. If DCR cannot be relaxed, switch the server to CIMD (client_id_metadata_document_supported) so registration is not needed

Example fix

# before (server policy)
allowed_redirect_uris: ["https://app.example.com/callback"]

# after
allowed_redirect_uris: ["https://app.example.com/callback", "http://localhost:*", "http://127.0.0.1:*"]
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side preflight: probe the registration endpoint cheaply before the full flow
async fn dcr_endpoint_ready(http: &Arc<dyn HttpClient>, endpoint: &Url) -> bool {
    let req = Request::builder().method(Method::OPTIONS).uri(endpoint.as_str()).body(AsyncBody::empty());
    matches!(http.send(req).await.map(|r| r.status().as_u16()), Ok(200 | 204 | 405))
}

Try / catch

match perform_dcr(&client, &registration_endpoint, &redirect_uri, grants).await {
    Err(err) if err.to_string().contains("DCR failed") => {
        let msg = err.to_string();
        if msg.contains("400") && msg.contains("redirect") {
            show_fix("allowlist loopback redirect URIs (http://127.0.0.1:*) on the auth server");
        } else if msg.contains("401") || msg.contains("403") {
            show_fix("DCR is gated (token/policy) on the auth server");
        }
        Err(err)
    }
    other => other,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/abe723f400061721. Report an issue: GitHub.