Kuberwastaken/claurst · error · anyhow::Error

Failed to open browser for OAuth

Error message

Failed to open browser for OAuth: {}

What it means

The `open` crate failed to launch the system browser (or URL handler) for the MCP OAuth authorization URL. The auth session had already bound its callback listener; the failure is environment-level — no default browser configured, headless/session environment without a URL opener, or the OS handler returned an error.

Solutions

  1. Set BROWSER / default browser environment variables and retry
  2. Copy the authorization URL from logs and open it manually in a browser within the 180s callback timeout
  3. On headless systems, run the auth flow from a machine with a desktop session
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at src-rust/crates/mcp/src/oauth.rs:305 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/70cca9aadc1309ca. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/oauth.rs:305

            .query_pairs()
            .find(|(key, _)| key == "state")
            .map(|(_, value)| value.to_string());
        if received_state.as_deref() != Some(expected_state) {
            anyhow::bail!("OAuth state mismatch — possible CSRF attack");
        }
    }

    parsed_url
        .query_pairs()
        .find(|(key, _)| key == "code")
        .map(|(_, value)| value.to_string())
        .ok_or_else(|| anyhow::anyhow!("OAuth callback did not contain an authorization code"))
}

pub async fn run_mcp_auth_session(session: McpAuthSession) -> anyhow::Result<McpAuthResult> {
    let (listener, host, callback_path) = bind_callback_listener(&session.redirect_uri).await?;
    open::that(&session.auth_url)
        .map_err(|e| anyhow::anyhow!("Failed to open browser for OAuth: {}", e))?;

    let code = wait_for_authorization_code(listener, &host, &callback_path, None).await?;
    let mut token = exchange_code(
        &session.metadata.token_endpoint,
        &code,
        &session.verifier,
        &session.redirect_uri,
    )
    .await?;
    token.server_name = session.server_name.clone();
    store_mcp_token(&token).map_err(|e| {
        anyhow::anyhow!(
            "Failed to store MCP token for '{}': {}",
            session.server_name,
            e
        )
    })?;

View on GitHub (pinned to b0637c97ec)