jlcodes99/cockpit-tools · error
[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={}
Error message
[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={} What it means
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.
Source
Thrown at crates/cockpit-core/src/modules/windsurf_oauth.rs:126
clear_pending_if_matches(&state.login_id, &state.state);
return;
}
if state.access_token.is_some() || state.callback_error.is_some() {
return;
}
match TcpListener::bind(("127.0.0.1", state.port)) {
Ok(listener) => {
drop(listener);
let callback_login_id = state.login_id.clone();
let callback_state = state.state.clone();
let callback_port = state.port;
tokio::spawn(async move {
if let Err(e) =
start_callback_server(callback_port, callback_login_id.clone(), callback_state)
.await
{
logger::log_error(&format!(
"[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={}",
callback_login_id, e
));
}
});
logger::log_info(&format!(
"[Windsurf OAuth] 已恢复本地回调服务: login_id={}, port={}",
state.login_id, state.port
));
}
Err(err) if err.kind() == ErrorKind::AddrInUse => {
logger::log_info(&format!(
"[Windsurf OAuth] 本地回调端口已占用,视为监听中: login_id={}, port={}",
state.login_id, state.port
));
}
Err(err) => {
logger::log_warn(&format!(View on GitHub (pinned to 1ed8b77992)
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.
Example fix
// before: blindly reusing the saved port from prior state
let callback_port = state.port;
tokio::spawn(start_callback_server(callback_port, ...));
// after: detect bind failure and surface it so the user re-logs-in
if !port_is_free(callback_port) {
logger::log_error("callback port busy; cancel pending login and start a new one");
return;
} Defensive patterns
Strategy: fallback
Validate before calling
fn port_is_free(port: u16) -> bool {
std::net::TcpListener::bind(("127.0.0.1", port)).is_ok()
}
if !port_is_free(state.port) {
eprintln!("callback port {} busy — cancel pending login and retry", state.port);
} Try / catch
match start_callback_server(callback_port, callback_login_id, callback_state).await {
Ok(()) => {}
Err(e) => {
logger::log_error(&format!(
"[Windsurf OAuth] 回调服务恢复失败: login_id={}, error={}",
callback_login_id, e
));
// mark this login_id as failed so the UI prompts a fresh login
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- [Windsurf OAuth] 回调服务异常: login_id={}, error={}
- 本地回调服务异常: {}
- OAuth 回调服务器错误: {}
- 默认 OAuth client 配置无效
- 无效的 Auth URL
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/1e9afc729af8513a.
Report an issue: GitHub.