NousResearch/hermes-agent · error

OAuth popup was blocked — allow popups for this dashboard an

Error message

OAuth popup was blocked — allow popups for this dashboard and retry

What it means

completeMcpDashboardOAuth opens an about:blank popup synchronously inside the click handler (browsers block popups opened after an await). If window.open returns null the browser's popup blocker prevented it, and the OAuth flow cannot proceed because there is no window to navigate to the authorization URL.

Source

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

  maxPollFailures?: number;
};

const defaultSleep = (milliseconds: number) =>
  new Promise<void>((resolve) => window.setTimeout(resolve, milliseconds));

export async function completeMcpDashboardOAuth({
  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;

View on GitHub (pinned to c896c09c42)

Solutions

  1. Allow popups for the dashboard origin in the browser's site settings, then click the OAuth button again.
  2. Ensure the flow is initiated from a real click event with no await before open() — the code already requires this ordering.
  3. If embedded in a webview/iframe, open the dashboard in a top-level tab.
Defensive patterns

Strategy: validation

Validate before calling

const probe = window.open('', '_blank')
if (!probe) {
  showBanner('Allow popups for this dashboard to connect MCP OAuth servers')
  probe?.close()
  return
}
probe.close()
await completeMcpDashboardOAuth({ ... })

Try / catch

try {
  await completeMcpDashboardOAuth({ serverName, start, status, open })
} catch (err) {
  if (String(err).includes('popup was blocked')) {
    toast('Popups are blocked — enable them for this site, then retry')
    return
  }
  throw err
}

Prevention

When it happens

Trigger: Calling completeMcpDashboardOAuth outside a direct user-gesture call stack, the browser blocking all popups for the origin, or an embedded webview that does not support window.open.

Common situations: Invoking the MCP OAuth button programmatically (from a test or an effect), a browser extension blocking popups, or the dashboard embedded in an iframe/webview with popup denial.

Related errors


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