Hmbown/CodeWhale · error · io::Error
control socket already live at
Error message
control socket already live at {} What it means
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.
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
Example fix
// before
let handle = bind_control_socket(&dir, &id, tx, status)?;
// after
match bind_control_socket(&dir, &id, tx, status) {
Ok(h) => h,
Err(e) if e.kind() == io::ErrorKind::AddrInUse => {
eprintln!("session {id} is already active in another instance");
return;
}
Err(e) => return Err(e.into()),
} Defensive patterns
Strategy: try-catch
Validate before calling
// probe before binding
if let Ok(_stream) = UnixStream::connect(&socket_path) {
eprintln!("session already live; refusing to start a second instance");
std::process::exit(0);
} Try / catch
match bind_control_socket(&dir, &id, tx, status) {
Err(e) if e.kind() == io::ErrorKind::AddrInUse => {
eprintln!("control socket already live at {}", socket_path.display());
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot open session : its queued input is already open in…
- session ' ' is open in an interactive Codewhale session…
- A saved trace identifier collided. Try saving again.
- a window change is already in progress
- another Codewhale process
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/9be08381a96870d3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/control_socket.rs:679
))
}
/// Take over the socket path, or refuse when a live server already holds it.
#[cfg(unix)]
fn prepare_socket_path(path: &Path) -> io::Result<()> {
match fs::symlink_metadata(path) {
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()),
Err(error) => Err(error),
Ok(metadata) => {
if !metadata.file_type().is_socket() {
// A plain file (or directory) in the way: not ours to keep.
fs::remove_file(path)?;
return Ok(());
}
match UnixStream::connect(path) {
// Someone answers: a live process owns this session's socket.
// Do not steal it (a "socket busy" refusal).
Ok(_) => Err(io::Error::new(
io::ErrorKind::AddrInUse,
format!("control socket already live at {}", path.display()),
)),
// Stale: the file exists but nothing listens. Take over.
Err(_) => {
fs::remove_file(path)?;
Ok(())
}
}
}
}
}
/// (device, inode) so an unlink never removes a file this process did not bind.
#[cfg(unix)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SocketFileIdentity {
dev: u64,View on GitHub (pinned to 73e0f67d83)