jlcodes99/cockpit-tools · error

OAuth 回调服务器错误: {}

Error message

OAuth 回调服务器错误: {}

What it means

OAuth callback server failure log inside the restored listener spawned by ensure_callback_listener_for_state (src-tauri/src/modules/codex_oauth.rs:234). For a persisted OAuthState without a device_auth_id, the module re-binds the saved callback port and spawns start_callback_server on a tokio task; if that server task returns Err, this line logs it. The OAuth login flow then cannot receive the browser redirect callback, so the pending login will eventually time out. Called from start_oauth_login and restore_pending_oauth_listener.

Source

Thrown at src-tauri/src/modules/codex_oauth.rs:234

    match TcpListener::bind(("127.0.0.1", state.port)) {
        Ok(listener) => {
            drop(listener);
            let expected_state = state.state.clone();
            let expected_login_id = state.login_id.clone();
            let callback_url = state.redirect_uri.clone();
            let app_handle_clone = app_handle.clone();
            let port = state.port;
            tokio::spawn(async move {
                if let Err(e) = start_callback_server(
                    port,
                    expected_state,
                    expected_login_id,
                    callback_url,
                    app_handle_clone,
                )
                .await
                {
                    logger::log_error(&format!("OAuth 回调服务器错误: {}", e));
                }
            });
            logger::log_info(&format!(
                "Codex OAuth 已恢复回调监听: login_id={}, port={}",
                state.login_id, state.port
            ));
        }
        Err(err) if err.kind() == ErrorKind::AddrInUse => {
            logger::log_info(&format!(
                "Codex OAuth 回调端口已占用,视为监听中: login_id={}, port={}",
                state.login_id, state.port
            ));
        }
        Err(err) => {
            logger::log_warn(&format!(
                "Codex OAuth 回调监听恢复失败: login_id={}, port={}, error={}",
                state.login_id, state.port, err
            ));

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Check whether another process is listening on the callback port (e.g. `lsof -iTCP:<port>` / `netstat -ano`) and kill it or change OAUTH_CALLBACK_PORT
  2. Restart the OAuth login (start_oauth_login) to obtain a fresh state and re-establish the listener
  3. Disable or configure antivirus/firewall software that blocks loopback socket binds
  4. If the state expired, note that ensure_callback_listener_for_state clears it when expires_at <= now — just log in again
  5. Retry after closing other instances of the app that may race for the same port

Example fix

// before: fire-and-forget callback server, failure only logged
tokio::spawn(async move {
    if let Err(e) = start_callback_server(port, state, login_id, url, handle).await {
        log_error("OAuth 回调服务器错误: {}", e);
    }
});
// after: probe the bind inside the task and report the failure to the UI
tokio::spawn(async move {
    match start_callback_server(port, state, login_id, url, handle.clone()).await {
        Ok(()) => {}
        Err(e) => {
            log_error("OAuth 回调服务器错误: {}", e);
            let _ = handle.emit("codex:oauth-callback-error", serde_json::json!({
                "login_id": login_id, "port": port, "error": e
            }));
        }
    }
});
Defensive patterns

Strategy: try-catch

Validate before calling

use std::net::TcpListener;
fn callback_port_available(port: u16) -> bool {
    TcpListener::bind(("127.0.0.1", port)).map(|l| drop(l)).is_ok()
}
// check before starting/restoring OAuth login: callback_port_available(state.port)

Type guard

fn listener_restorable(state: &OAuthState) -> bool {
    state.device_auth_id.is_none() && state.expires_at > now_timestamp()
}

Try / catch

match start_oauth_login(app_handle).await {
    Ok(login_id) => println!("OAuth 登录已启动: {login_id}"),
    Err(e) if e.contains("端口被占用") || e.contains("port in use") => {
        eprintln!("回调端口被占用,请关闭占用进程后重试: {e}");
    }
    Err(e) => eprintln!("OAuth 登录失败: {e}"),
}

Prevention

When it happens

Trigger: start_callback_server(port, expected_state, expected_login_id, callback_url, ...) returning Err after the pre-bind probe succeeded — e.g. another process steals port 127.0.0.1:callback_port between the probe and the server bind, the listener is closed unexpectedly, an internal accept/bind error occurs, or the tokio runtime fails to accept connections.

Common situations: Antivirus/firewall blocking loopback binds; another instance of the app (or Codex CLI) already holding the callback port in a state that passed the probe; port reused by an unrelated dev server on 127.0.0.1; the persisted OAuthState restored after app restart while a stale server still occupies the port; corporate security software intercepting loopback HTTP.

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/3a9986915b03dbca. Report an issue: GitHub.