Hmbown/CodeWhale · error

invalid MCP OAuth callback port 0

Error message

invalid MCP OAuth callback port 0

What it means

When constructing the OAuth callback listener, an explicit `callback_port` of `0` is rejected. Port `None` means 'pick an ephemeral port', but an explicit `Some(0)` is treated as a configuration mistake rather than being silently mapped to ephemeral.

Solutions

  1. Set the callback port to a concrete value (e.g. 8080–9999) or omit the field entirely to request an ephemeral port
  2. Fix whatever produced `Some(0)` — replace explicit 0 with `None` in the calling code
  3. Validate the configured port is in 1..=65535 at config-load time

Example fix

// before
callback_port: Some(0)
// after
callback_port: None // ephemeral port, or Some(8642) for a fixed port
Defensive patterns

Strategy: validation

Validate before calling

if let Some(0) = callback_port {
    return Err(anyhow!("callback_port must be 1-65535 or unset"));
}

Type guard

fn valid_callback_port(p: Option<u16>) -> Option<u16> {
    p.filter(|&p| p != 0)
}

Prevention

When it happens

Trigger: Passing `callback_port: Some(0)` into the OAuth flow constructor (e.g. from a config value of 0 for the MCP OAuth callback port).

Common situations: A config file with `callback_port = 0` intending 'auto-pick'; a port computed by a script that defaulted to 0 on lookup failure.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/72ece2bf9d133811. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/oauth.rs:1588

    server_name: String,
    server_url: String,
}

impl OauthLoginFlow {
    #[allow(clippy::too_many_arguments)]
    async fn new(
        server_name: &str,
        server_url: &str,
        client: McpHttpClient,
        scopes: &[String],
        oauth_client_id: Option<&str>,
        oauth_resource: Option<&str>,
        callback_port: Option<u16>,
        callback_url: Option<&str>,
    ) -> Result<Self> {
        let bind_host = callback_bind_host(callback_url);
        let bind_addr = match callback_port {
            Some(0) => bail!("invalid MCP OAuth callback port 0"),
            Some(port) => format!("{bind_host}:{port}"),
            None => format!("{bind_host}:0"),
        };
        let listener = TcpListener::bind(&bind_addr)
            .await
            .map_err(|err| anyhow!(err))?;
        let redirect_uri = resolve_redirect_uri(&listener, callback_url)?;
        let callback_id = callback_id_from_server_url(server_url)?;
        let redirect_uri = append_callback_id_to_redirect_uri(&redirect_uri, &callback_id)?;
        let callback_path = callback_path_from_redirect_uri(&redirect_uri)?;

        let (tx, rx) = oneshot::channel();
        let guard = CallbackServerGuard {
            accept_task: spawn_callback_server(listener, tx, callback_path),
        };

        let scope_refs: Vec<&str> = scopes.iter().map(String::as_str).collect();
        let oauth_state = start_authorization(

View on GitHub (pinned to 73e0f67d83)