nexu-io/open-design · error · Error

dynamic client registration failed: HTTP

Error message

dynamic client registration failed: HTTP ${res.status} ${res.statusText} ${txt}

What it means

registerClient POSTs a Dynamic Client Registration (DCR) request to the auth server's registration_endpoint. Any non-2xx response is surfaced as a failure along with status, statusText, and up to 500 chars of body. Causes range from the endpoint rejecting DCR (4xx) to network or server errors (5xx).

Solutions

  1. Inspect the embedded status and body text in the message to see the server's reason.
  2. Confirm registration_endpoint is correct and reachable.
  3. Verify redirectUri is permitted by the provider.
  4. If DCR is unsupported, pre-register a client and seed the cache (see getOrRegisterClient).
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await registerClient(registrationEndpoint, redirectUri);
} catch (e) {
  if (/dynamic client registration failed/i.test(e.message)) {
    // message carries HTTP status + up to 500 chars of body
    logOAuthDetail(e.message);
    // for providers that reject DCR, fall back to a pre-registered client
  }
  throw e;
}

Prevention

When it happens

Trigger: Auth server rejected DCR with 400, 401, or 403; registration_endpoint is wrong or unreachable; server returned 5xx; rate limited with 429.

Common situations: An MCP provider does not actually support DCR despite advertising an endpoint; a corporate proxy returns 4xx; the redirectUri is not allowlisted.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/0cf6e062075c89a5. Report an issue: GitHub.

Appendix: source

Thrown at apps/daemon/src/mcp-oauth.ts:269

  const body = {
    redirect_uris: [redirectUri],
    token_endpoint_auth_method: 'none',
    grant_types: ['authorization_code', 'refresh_token'],
    response_types: ['code'],
    client_name: 'Open Design',
    application_type: 'web',
  };
  const res = await fetchImpl(registrationEndpoint, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      accept: 'application/json',
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const txt = await safeText(res);
    throw new Error(
      `dynamic client registration failed: HTTP ${res.status} ${res.statusText} ${txt}`,
    );
  }
  const json = (await res.json()) as { client_id?: string; client_secret?: string };
  if (!json.client_id) {
    throw new Error('dynamic client registration response missing client_id');
  }
  const out: { clientId: string; clientSecret?: string } = { clientId: json.client_id };
  if (json.client_secret) out.clientSecret = json.client_secret;
  return out;
}

/**
 * Cached version of `registerClient`. Looks up `(authServerIssuer, redirectUri)`
 * in the cache file and re-uses the existing client; falls back to a fresh
 * DCR call when nothing is cached.
 */
export async function getOrRegisterClient(

View on GitHub (pinned to 5be4028344)