NousResearch/hermes-agent · error · Error

OAuth failed to start

Error message

OAuth failed to start

What it means

This is the fallback message when start(serverName) — the backend call that initiates the MCP server's OAuth flow — returns `{status: 'error'}` with an empty or missing `error` field. The backend rejected the flow start but gave no reason, so the client shows the generic text. The popup is closed and the error rethrown.

Source

Thrown at web/src/lib/mcp-dashboard-oauth.ts:34

  serverName,
  start,
  status,
  open,
  sleep = defaultSleep,
  maxPollFailures = 3,
}: CompleteOptions): Promise<McpOAuthFlow> {
  // Open synchronously from the click handler, before the first await. Browsers
  // otherwise classify the later OAuth popup as unsolicited and block it.
  const authWindow = open("about:blank", "_blank") as Window | null;
  if (!authWindow) {
    throw new Error("OAuth popup was blocked — allow popups for this dashboard and retry");
  }
  authWindow.opener = null;
  let started: McpOAuthFlow;
  try {
    started = await start(serverName);
    if (started.status === "error") {
      throw new Error(started.error || "OAuth failed to start");
    }
    if (!started.authorization_url) {
      throw new Error("OAuth server did not provide an authorization URL");
    }
    authWindow.location.href = started.authorization_url;
  } catch (error) {
    authWindow.close();
    throw error;
  }

  let pollFailures = 0;
  for (;;) {
    let current: McpOAuthFlow;
    try {
      current = await status(started.flow_id);
      pollFailures = 0;
    } catch (error) {
      pollFailures += 1;

View on GitHub (pinned to c896c09c42)

Solutions

  1. Check the gateway logs at the moment of the failed start — the server-side exception carries the real reason the client didn't receive.
  2. Verify the MCP server's OAuth config (issuer/client metadata) is complete and the server is reachable from the gateway.
  3. Update the gateway so its start endpoint includes a descriptive `error` field, making future failures self-explanatory.
Defensive patterns

Strategy: try-catch

Type guard

function isStartError(r: unknown): r is { status: 'error'; error?: string } {
  return !!r && (r as { status?: string }).status === 'error'
}

Try / catch

try {
  await completeMcpDashboardOAuth({ serverName, start, status, open })
} catch (err) {
  if (String(err) === 'OAuth failed to start') {
    toast('Gateway refused the OAuth start — check gateway logs for this server')
  }
  throw err
}

Prevention

When it happens

Trigger: POSTing the MCP OAuth start endpoint for a server with no OAuth metadata configured, a server entry whose client_id is missing, or a gateway-side OAuth handler that fails before producing an authorization URL but returns an error response without a message.

Common situations: Adding an MCP server whose OAuth client credentials were never configured; gateway version skew where the start endpoint exists but errors; a server marked for OAuth that actually uses no auth.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/4982f4415ff58a8d. Report an issue: GitHub.