{"record":{"id":"1e9afc729af8513a","repo":"jlcodes99/cockpit-tools","slug":"windsurf-oauth-login-id-error","errorCode":null,"errorMessage":"[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={}","messagePattern":"\\[Windsurf OAuth\\] 回调服务恢复失败: login_id=(.+?), error=(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cockpit-core/src/modules/windsurf_oauth.rs","lineNumber":126,"sourceCode":"        clear_pending_if_matches(&state.login_id, &state.state);\n        return;\n    }\n    if state.access_token.is_some() || state.callback_error.is_some() {\n        return;\n    }\n\n    match TcpListener::bind((\"127.0.0.1\", state.port)) {\n        Ok(listener) => {\n            drop(listener);\n            let callback_login_id = state.login_id.clone();\n            let callback_state = state.state.clone();\n            let callback_port = state.port;\n            tokio::spawn(async move {\n                if let Err(e) =\n                    start_callback_server(callback_port, callback_login_id.clone(), callback_state)\n                        .await\n                {\n                    logger::log_error(&format!(\n                        \"[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={}\",\n                        callback_login_id, e\n                    ));\n                }\n            });\n            logger::log_info(&format!(\n                \"[Windsurf OAuth] 已恢复本地回调服务: login_id={}, port={}\",\n                state.login_id, state.port\n            ));\n        }\n        Err(err) if err.kind() == ErrorKind::AddrInUse => {\n            logger::log_info(&format!(\n                \"[Windsurf OAuth] 本地回调端口已占用，视为监听中: login_id={}, port={}\",\n                state.login_id, state.port\n            ));\n        }\n        Err(err) => {\n            logger::log_warn(&format!(","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/crates/cockpit-core/src/modules/windsurf_oauth.rs#L108-L144","documentation":"Logged by the spawned task in ensure_callback_server_for_state when start_callback_server fails while (re)starting the local OAuth callback HTTP listener. This path runs for pending OAuth logins restored at startup (restore_pending_oauth_listener) or from start_login, so a failure means the Windsurf OAuth flow cannot receive its redirect and that login will not complete. The error value comes from binding/serving the callback port.","triggerScenarios":"Calling start_login or restore_pending_oauth_listener when the saved callback port cannot be bound: another process already listens on that port, the process lacks permission to bind, or the listener fails while serving the callback request.","commonSituations":"A previous cockpit instance (or zombie process) still holding the callback port after a crash; restarting the app before the old socket is released (TIME_WAIT); the port colliding with another local dev server; running in a sandbox that forbids binding the saved port.","solutions":["Check the logged error text: 'Address already in use' means find and kill the process holding the port (lsof -i :<port>).","Clear/restart the pending OAuth login so a fresh login_id and callback state are used instead of the stale one.","Restart cockpit after the port is free, or configure a different callback port if the mechanism supports it.","Retry the Windsurf login from scratch once the listener can bind; the restored flow is dead once this fires."],"exampleFix":"// before: blindly reusing the saved port from prior state\nlet callback_port = state.port;\ntokio::spawn(start_callback_server(callback_port, ...));\n// after: detect bind failure and surface it so the user re-logs-in\nif !port_is_free(callback_port) {\n    logger::log_error(\"callback port busy; cancel pending login and start a new one\");\n    return;\n}","handlingStrategy":"fallback","validationCode":"fn port_is_free(port: u16) -> bool {\n    std::net::TcpListener::bind((\"127.0.0.1\", port)).is_ok()\n}\nif !port_is_free(state.port) {\n    eprintln!(\"callback port {} busy — cancel pending login and retry\", state.port);\n}","typeGuard":null,"tryCatchPattern":"match start_callback_server(callback_port, callback_login_id, callback_state).await {\n    Ok(()) => {}\n    Err(e) => {\n        logger::log_error(&format!(\n            \"[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={}\",\n            callback_login_id, e\n        ));\n        // mark this login_id as failed so the UI prompts a fresh login\n    }\n}","preventionTips":["Before restoring a pending login, probe the saved port and abort the stale login if it cannot be bound.","Clean up pending OAuth state (login_id, port) on graceful shutdown so restore never races a live listener.","Kill leftover cockpit processes after crashes before restarting the OAuth flow.","Surface a 're-login required' action in the UI instead of leaving the pending login dangling."],"tags":["oauth","windsurf","callback-server","port-bind","rust"],"backgroundTag":"address-already-in-use","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}