{"record":{"id":"4982f4415ff58a8d","repo":"NousResearch/hermes-agent","slug":"oauth-failed-to-start","errorCode":null,"errorMessage":"OAuth failed to start","messagePattern":"OAuth failed to start","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/lib/mcp-dashboard-oauth.ts","lineNumber":34,"sourceCode":"  serverName,\n  start,\n  status,\n  open,\n  sleep = defaultSleep,\n  maxPollFailures = 3,\n}: CompleteOptions): Promise<McpOAuthFlow> {\n  // Open synchronously from the click handler, before the first await. Browsers\n  // otherwise classify the later OAuth popup as unsolicited and block it.\n  const authWindow = open(\"about:blank\", \"_blank\") as Window | null;\n  if (!authWindow) {\n    throw new Error(\"OAuth popup was blocked — allow popups for this dashboard and retry\");\n  }\n  authWindow.opener = null;\n  let started: McpOAuthFlow;\n  try {\n    started = await start(serverName);\n    if (started.status === \"error\") {\n      throw new Error(started.error || \"OAuth failed to start\");\n    }\n    if (!started.authorization_url) {\n      throw new Error(\"OAuth server did not provide an authorization URL\");\n    }\n    authWindow.location.href = started.authorization_url;\n  } catch (error) {\n    authWindow.close();\n    throw error;\n  }\n\n  let pollFailures = 0;\n  for (;;) {\n    let current: McpOAuthFlow;\n    try {\n      current = await status(started.flow_id);\n      pollFailures = 0;\n    } catch (error) {\n      pollFailures += 1;","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/web/src/lib/mcp-dashboard-oauth.ts#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the gateway logs at the moment of the failed start — the server-side exception carries the real reason the client didn't receive.","Verify the MCP server's OAuth config (issuer/client metadata) is complete and the server is reachable from the gateway.","Update the gateway so its start endpoint includes a descriptive `error` field, making future failures self-explanatory."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isStartError(r: unknown): r is { status: 'error'; error?: string } {\n  return !!r && (r as { status?: string }).status === 'error'\n}","tryCatchPattern":"try {\n  await completeMcpDashboardOAuth({ serverName, start, status, open })\n} catch (err) {\n  if (String(err) === 'OAuth failed to start') {\n    toast('Gateway refused the OAuth start — check gateway logs for this server')\n  }\n  throw err\n}","preventionTips":["Correlate failures with gateway logs using the server name and timestamp.","Verify OAuth client metadata is configured before offering the OAuth button.","Improve the backend to always include a descriptive error field."],"tags":["oauth","mcp","backend"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}