{"record":{"id":"baef0bec8b7da305","repo":"Hmbown/CodeWhale","slug":"unable-to-bind-name-oauth-callback-ports","errorCode":null,"errorMessage":"unable to bind {name} OAuth callback ports","messagePattern":"unable to bind (.+?) OAuth callback ports","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/oauth.rs","lineNumber":1316,"sourceCode":"        }\n        if !bound.is_empty() {\n            return Ok(bound);\n        }\n    }\n    let ports = params\n        .loopback_ports\n        .iter()\n        .map(u16::to_string)\n        .collect::<Vec<_>>()\n        .join(\" or \");\n    let hint = if params.callback_conflict_hint.is_empty() {\n        String::new()\n    } else {\n        format!(\" {}\", params.callback_conflict_hint)\n    };\n    Err(last_error\n        .map(anyhow::Error::from)\n        .unwrap_or_else(|| anyhow::anyhow!(\"unable to bind {name} OAuth callback ports\")))\n    .with_context(|| format!(\"{name} sign-in needs loopback port {ports}.{hint}\"))\n}\n\npub(crate) fn start_auth_request_on(\n    listeners: &[TcpListener],\n    params: &OAuthProviderParams,\n    inputs: &ResolvedOAuthInputs,\n) -> Result<BrowserAuthRequest> {\n    let port = listeners\n        .first()\n        .with_context(|| {\n            format!(\n                \"{} OAuth callback has no bound listener\",\n                params.display_name\n            )\n        })?\n        .local_addr()\n        .with_context(|| {","sourceCodeStart":1298,"sourceCodeEnd":1334,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/oauth.rs#L1298-L1334","documentation":"The OAuth flow binds one or more fixed loopback TCP ports to receive the provider's redirect. When every candidate port failed to bind, this error is thrown (wrapped with a context naming the required ports and a conflict hint). It means no listener could be opened, so sign-in cannot proceed.","triggerScenarios":"Calling start_auth_request_on/bind for the provider's callback ports when all of them return bind errors (last_error is this message only when no underlying bind error was captured, i.e. the port list was empty or errors were dropped).","commonSituations":"Another instance of Codewhale (or another app like a dev server) already listening on the provider's fixed loopback ports; stale sign-in process holding the socket; running inside a container/sandbox where loopback binding is restricted; TIME_WAIT exhaustion.","solutions":["Close the process occupying the callback port: lsof -i :<port> then kill it, or stop other Codewhale instances.","Re-run the sign-in command; transient TIME_WAIT usually clears in seconds.","Check the provider's callback_conflict_hint in the error context for port-specific guidance.","If sandboxed (container, CI), ensure loopback TCP listening is permitted."],"exampleFix":"// before: second instance while one is already signed in\ncodewhale auth login &   # port already held by first instance\n// after\npkill -f 'codewhale auth' && codewhale auth login","handlingStrategy":"retry","validationCode":"const free = await new Promise(r => { const s = require('net').createServer(); s.once('error', () => r(false)); s.listen(port, '127.0.0.1', () => s.close(() => r(true))); });","typeGuard":null,"tryCatchPattern":"try { await signIn(); } catch (e) { if (String(e).includes('unable to bind')) { await new Promise(r => setTimeout(r, 1000)); return signIn(); } throw e; }","preventionTips":["Stop other Codewhale instances before signing in","Check lsof -i :<port> when sign-in fails repeatedly","Ensure your sandbox/container permits loopback TCP listeners"],"tags":["oauth","network","port-binding"],"backgroundTag":"address-already-in-use","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}