{"record":{"id":"2de90102356ba715","repo":"zeroclaw-labs/zeroclaw","slug":"local-ipc-endpoint-is-already-serving-at","errorCode":null,"errorMessage":"local IPC endpoint is already serving at {}","messagePattern":"local IPC endpoint is already serving at (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/rpc/local.rs","lineNumber":474,"sourceCode":"        if let Some(parent) = path.parent() {\n            tokio::fs::create_dir_all(parent).await?;\n            use std::os::unix::fs::PermissionsExt;\n            tokio::fs::set_permissions(parent, std::fs::Permissions::from_mode(0o700))\n                .await\n                .ok();\n        }\n        Ok(())\n    }\n\n    pub async fn remove_stale(path: &Path) -> Result<()> {\n        let observed = match tokio::fs::symlink_metadata(path).await {\n            Ok(metadata) => SocketIdentity::from_metadata(&metadata),\n            Err(error) if error.kind() == ErrorKind::NotFound => return Ok(()),\n            Err(error) => return Err(error).context(\"inspecting local IPC endpoint\"),\n        };\n\n        match UnixStream::connect(path).await {\n            Ok(_) => Err(std::io::Error::new(\n                ErrorKind::AddrInUse,\n                format!(\n                    \"local IPC endpoint is already serving at {}\",\n                    path.display()\n                ),\n            )\n            .into()),\n            Err(error) if error.kind() == ErrorKind::NotFound => Ok(()),\n            Err(error) if error.kind() != ErrorKind::ConnectionRefused => {\n                Err(error).context(\"probing existing local IPC endpoint\")\n            }\n            Err(_) => {\n                let current = match tokio::fs::symlink_metadata(path).await {\n                    Ok(metadata) => SocketIdentity::from_metadata(&metadata),\n                    Err(error) if error.kind() == ErrorKind::NotFound => return Ok(()),\n                    Err(error) => {\n                        return Err(error).context(\"rechecking stale local IPC endpoint\");\n                    }","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/rpc/local.rs#L456-L492","documentation":"Raised by remove_stale (crates/zeroclaw-runtime/src/rpc/local.rs:474, called from bind_locked before UnixListener::bind) when the socket path exists and a probe UnixStream::connect(path) succeeds. A successful connect proves a live server is accepting connections on that endpoint, so zeroclaw refuses to treat the socket as stale debris and reports ErrorKind::AddrInUse rather than unlinking a working endpoint out from under its owner.","triggerScenarios":"Calling bind(path) (via bind_locked -> remove_stale) when some process is actively listening on the path and accepting connections. Typical when the socket lives in a shared/external directory (e.g. ZEROCLAW_SOCKET pointing into /tmp) where the lifecycle lock does not confer exclusive ownership, or when a non-cooperating process (another service, a leftover agent of a different version) bound the same path.","commonSituations":"Pointing two different daemon deployments at the same ZEROCLAW_SOCKET in a world-writable dir like /tmp; a socket path colliding with another application's Unix socket; running against a socket served by a container/host process the caller does not manage; leftover socket from a daemon started outside systemd while configuring a new unit.","solutions":["Identify and stop the existing listener: `ss -xlp | grep <path>` or `lsof <path>` shows the PID; stop that process (or reuse it as your endpoint).","If both services are legitimately needed, move one to a different socket path (own data dir or unique ZEROCLAW_SOCKET).","Prefer the default per-data-dir socket (data_dir/daemon.sock) whose lifecycle lock already gives single-ownership, instead of hand-picked paths in shared directories.","If the other listener is orphaned, kill its PID; once it exits, connect probes fail with ECONNREFUSED and remove_stale will clean the path automatically."],"exampleFix":"// before: bind onto a path another live server is using\nlet (listener, guard) = rpc::local::bind(Path::new(\"/tmp/shared.sock\")).await?; // AddrInUse: already serving\n\n// after: probe first and fail with a clear diagnosis (or pick another path)\nlet path = Path::new(\"/tmp/shared.sock\");\nif tokio::net::UnixStream::connect(path).await.is_ok() {\n    anyhow::bail!(\"another server is already serving at {}; stop it or choose a different ZEROCLAW_SOCKET\", path.display());\n}\nlet (listener, guard) = rpc::local::bind(path).await?;","handlingStrategy":"validation","validationCode":"use tokio::net::UnixStream;\n\nasync fn socket_has_live_server(path: &std::path::Path) -> bool {\n    // remove_stale uses exactly this probe: connect() success == live listener.\n    UnixStream::connect(path).await.is_ok()\n}\n\n// if socket_has_live_server(&path).await { /* reuse it or bail before bind() */ }","typeGuard":"fn is_addr_in_use(err: &anyhow::Error) -> bool {\n    err.chain()\n        .filter_map(|c| c.downcast_ref::<std::io::Error>())\n        .any(|e| e.kind() == std::io::ErrorKind::AddrInUse)\n}","tryCatchPattern":"match rpc::local::bind(&path).await {\n    Ok(bound) => { /* serve */ }\n    Err(e) if is_addr_in_use(&e) => {\n        eprintln!(\"a live server is already accepting at {}; reuse it or pick another path\", path.display());\n        std::process::exit(1);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Prefer the default per-data-dir daemon.sock, whose lifecycle lock makes this conflict impossible, over hand-picked paths in shared directories.","Do not point multiple deployments at one ZEROCLAW_SOCKET; namespace sockets per service or per user.","Before binding, probe the socket with a connect and reuse the existing daemon when it answers.","When reusing external socket dirs like /tmp, include an instance-unique name (pid/user/app) to avoid collisions."],"tags":["rust","unix-socket","daemon","endpoint-conflict","stale-socket"],"backgroundTag":"address-in-use","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}