zeroclaw-labs/zeroclaw · error

local IPC endpoint lock {} was replaced while being acquired

Error message

local IPC endpoint lock {} was replaced while being acquired; refusing to share lifecycle ownership

What it means

The dev/inode identity of `<socket>.lock` changed between opening the file and taking the flock: something replaced the directory entry mid-acquisition. Accepting the replacement would let a second daemon lock a different inode and both believe they own socket lifecycle cleanup, so ZeroClaw bails instead of sharing ownership.

Source

Thrown at crates/zeroclaw-runtime/src/rpc/local.rs:398

                    lock_path.display()
                )
            })?;
            require_trusted_lock_file(&metadata, &lock_path)?;

            match file.try_lock() {
                Ok(()) => {
                    // The directory entry must still name the locked inode.
                    // A swap between open and lock would let a second daemon
                    // lock the replacement and re-enter the split-ownership
                    // race this lock exists to prevent.
                    let current = SocketIdentity::read(&lock_path).with_context(|| {
                        format!(
                            "confirming local IPC endpoint lifecycle lock {}",
                            lock_path.display()
                        )
                    })?;
                    if current != SocketIdentity::from_metadata(&metadata) {
                        anyhow::bail!(
                            "local IPC endpoint lock {} was replaced while being \
                             acquired; refusing to share lifecycle ownership",
                            lock_path.display()
                        );
                    }
                    Ok(Self { _file: file })
                }
                Err(TryLockError::WouldBlock) => Err(std::io::Error::new(
                    ErrorKind::AddrInUse,
                    format!(
                        "local IPC endpoint lifecycle is already owned at {}",
                        path.display()
                    ),
                )
                .into()),
                Err(TryLockError::Error(error)) => Err(error).with_context(|| {
                    format!(
                        "locking local IPC endpoint lifecycle at {}",

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Retry acquisition once — a fresh, stable lock usually resolves it.
  2. Stop whatever replaces the lock: remove rm/recreate logic for the socket dir from unit files and scripts (create-if-missing only).
  3. Ensure only one daemon start path exists (no overlapping systemd units/containers on the same ZEROCLAW_SOCKET).
  4. Point ZEROCLAW_SOCKET at a private directory no other tooling manages.
Defensive patterns

Strategy: validation

Validate before calling

// Detect active interference before start: if the lock identity keeps changing
// between two stats, something is replacing entries — investigate instead of racing it.
fn lock_identity_stable(sock: &std::path::Path) -> Option<(u64, u64)> {
    let mut s = sock.as_os_str().to_os_string();
    s.push(".lock");
    let p = std::path::PathBuf::from(s);
    let a = std::fs::symlink_metadata(&p).ok()?;
    std::thread::sleep(std::time::Duration::from_millis(50));
    let b = std::fs::symlink_metadata(&p).ok()?;
    (a.dev() == b.dev() && a.ino() == b.ino()).then_some((a.dev(), a.ino()))
}

Try / catch

Err(e) if e.to_string().contains("was replaced while being acquired") => {
    // something swaps the lock: stop scripts that rm/recreate the socket dir, then retry
}

Prevention

When it happens

Trigger: A cleanup process unlinks and recreates the lock exactly while the daemon starts; two daemons racing to start with one creating a fresh lock over the other's; configuration management atomically replacing files in the runtime dir (mv into place).

Common situations: Restart loops where a stopping daemon's cleanup recreates state while the next one starts; scripts that 'reset' the socket directory by rm+mkdir on every service start; hostile co-tenant in a shared directory swapping the lock.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/fad22d73976bf547. Report an issue: GitHub.