{"record":{"id":"9be08381a96870d3","repo":"Hmbown/CodeWhale","slug":"control-socket-already-live-at","errorCode":null,"errorMessage":"control socket already live at {}","messagePattern":"control socket already live at (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/control_socket.rs","lineNumber":679,"sourceCode":"    ))\n}\n\n/// Take over the socket path, or refuse when a live server already holds it.\n#[cfg(unix)]\nfn prepare_socket_path(path: &Path) -> io::Result<()> {\n    match fs::symlink_metadata(path) {\n        Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()),\n        Err(error) => Err(error),\n        Ok(metadata) => {\n            if !metadata.file_type().is_socket() {\n                // A plain file (or directory) in the way: not ours to keep.\n                fs::remove_file(path)?;\n                return Ok(());\n            }\n            match UnixStream::connect(path) {\n                // Someone answers: a live process owns this session's socket.\n                // Do not steal it (a \"socket busy\" refusal).\n                Ok(_) => Err(io::Error::new(\n                    io::ErrorKind::AddrInUse,\n                    format!(\"control socket already live at {}\", path.display()),\n                )),\n                // Stale: the file exists but nothing listens. Take over.\n                Err(_) => {\n                    fs::remove_file(path)?;\n                    Ok(())\n                }\n            }\n        }\n    }\n}\n\n/// (device, inode) so an unlink never removes a file this process did not bind.\n#[cfg(unix)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct SocketFileIdentity {\n    dev: u64,","sourceCodeStart":661,"sourceCodeEnd":697,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/control_socket.rs#L661-L697","documentation":"prepare_socket_path refuses to take over a socket path when another live process is already answering on it. It probes the path with UnixStream::connect; if the connection succeeds, a live server owns the session's socket and this AddrInUse error is returned instead of stealing it.","triggerScenarios":"Starting a second TUI instance bound to the same session id while the first is still running; attempting bind_control_socket when the session's socket file exists and accepts connections.","commonSituations":"Two terminal tabs opening the same session; a stale-looking session that is actually still live on another machine/SSH session; scripts that re-launch the TUI against a running instance.","solutions":["Check whether another Codewhale instance for this session is running and use it instead of starting a new one","Choose a different session id to get a fresh socket path","If you are certain nothing is live, verify with a connect probe and remove the socket file before rebinding","Handle ErrorKind::AddrInUse in the caller and surface 'session already active' to the user"],"exampleFix":"// before\nlet handle = bind_control_socket(&dir, &id, tx, status)?;\n// after\nmatch bind_control_socket(&dir, &id, tx, status) {\n    Ok(h) => h,\n    Err(e) if e.kind() == io::ErrorKind::AddrInUse => {\n        eprintln!(\"session {id} is already active in another instance\");\n        return;\n    }\n    Err(e) => return Err(e.into()),\n}","handlingStrategy":"try-catch","validationCode":"// probe before binding\nif let Ok(_stream) = UnixStream::connect(&socket_path) {\n    eprintln!(\"session already live; refusing to start a second instance\");\n    std::process::exit(0);\n}","typeGuard":null,"tryCatchPattern":"match bind_control_socket(&dir, &id, tx, status) {\n    Err(e) if e.kind() == io::ErrorKind::AddrInUse => {\n        eprintln!(\"control socket already live at {}\", socket_path.display());\n    }\n    other => other?,\n}","preventionTips":["Use a per-session lockfile alongside the socket","Check for a running instance before launching a second one","Clean up socket files on graceful shutdown","Surface 'session already active' clearly in the UI"],"tags":["io","socket","address-in-use","concurrency"],"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-22T06:17:15.046Z"}