NousResearch/hermes-agent · error · Error

OAuth server did not provide an authorization URL

Error message

OAuth server did not provide an authorization URL

What it means

The MCP OAuth start call succeeded (status was not 'error') but the returned flow object has no authorization_url, so there is nothing to navigate the popup to. This indicates a malformed or partial response from the gateway's OAuth start handler — the flow was registered but the provider's consent URL was never produced.

Source

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

  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;
      if (pollFailures >= maxPollFailures) throw error;
      await sleep(1000);
      continue;

View on GitHub (pinned to c896c09c42)

Solutions

  1. Inspect the start endpoint's raw JSON response in the network tab to confirm authorization_url is genuinely absent.
  2. Update/restart the gateway so its MCP OAuth handler matches the dashboard version and always returns authorization_url on success.
  3. Verify the MCP server's OAuth discovery metadata includes an authorization_endpoint.
Defensive patterns

Strategy: type-guard

Type guard

function isStartedFlow(v: unknown): v is { flow_id: string; authorization_url: string } {
  const f = v as { flow_id?: unknown; authorization_url?: unknown }
  return typeof f?.flow_id === 'string'
    && typeof f?.authorization_url === 'string'
    && f.authorization_url.length > 0
}

Try / catch

const started = await start(serverName)
if (!isStartedFlow(started)) {
  throw new Error(`OAuth start response incomplete: ${JSON.stringify(started)}`)
}

Prevention

When it happens

Trigger: A gateway bug or version where the start handler returns `{status:'ok', flow_id}` without building the authorization URL; an OAuth provider whose metadata lacks an authorization_endpoint so the URL cannot be constructed; response shape mismatch between gateway and web client.

Common situations: Version skew between dashboard frontend and gateway; an MCP server advertising OAuth (RFC 9725 metadata) but missing authorization_endpoint; middleware stripping fields from the JSON response.

Related errors


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