zeroclaw-labs/zeroclaw · error

local IPC endpoint lock {} is owned by uid {}, not the daemo

Error message

local IPC endpoint lock {} is owned by uid {}, not the daemon user; remove it or choose a different socket path

What it means

The existing lock file `<socket>.lock` is owned by a uid other than the daemon's effective uid. A foreign-owned entry could be locked by another local user to block startup, or unlinked and recreated by its owner to give two daemons different lock inodes, so ZeroClaw refuses to trust it.

Source

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

    /// Rejects pre-existing lock entries that do not provide the guarantees a
    /// freshly created lock would have.
    ///
    /// The inode must be a regular file owned by the current user with no
    /// group/other access, and still linked at the time of inspection. A
    /// foreign-owned or permissive entry could be locked by another local
    /// user to block startup, or unlinked and recreated by its owner to hand
    /// two daemons different lock inodes.
    fn require_trusted_lock_file(metadata: &Metadata, lock_path: &Path) -> Result<()> {
        if !metadata.file_type().is_file() {
            anyhow::bail!(
                "local IPC endpoint lock {} is not a regular file; remove it \
                 or choose a different socket path",
                lock_path.display()
            );
        }
        let euid = unsafe { libc::geteuid() };
        if metadata.uid() != euid {
            anyhow::bail!(
                "local IPC endpoint lock {} is owned by uid {}, not the \
                 daemon user; remove it or choose a different socket path",
                lock_path.display(),
                metadata.uid()
            );
        }
        if metadata.mode() & 0o077 != 0 {
            anyhow::bail!(
                "local IPC endpoint lock {} is accessible to other users \
                 (mode {:o}); restrict it to 0600 or remove it",
                lock_path.display(),
                metadata.mode() & 0o7777
            );
        }
        if metadata.nlink() == 0 {
            anyhow::bail!(
                "local IPC endpoint lock {} was unlinked while being opened",
                lock_path.display()

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Remove the stale lock: rm <socket>.lock (as root if needed).
  2. Or run the daemon as the uid that owns the existing lock file.
  3. Or choose a different socket path private to the current user.
  4. Standardize the service user so the lock is always created by the same account.

Example fix

# before: lock created by root, daemon now runs as zeroclaw
ls -l /run/zeroclaw/zeroclaw.sock.lock   # -rw------- 1 root root ...

# after
sudo rm /run/zeroclaw/zeroclaw.sock.lock
systemctl restart zeroclaw
Defensive patterns

Strategy: validation

Validate before calling

use std::os::unix::fs::MetadataExt;
fn lock_owned_by_me(sock: &std::path::Path) -> bool {
    let mut s = sock.as_os_str().to_os_string();
    s.push(".lock");
    let Ok(md) = std::fs::symlink_metadata(std::path::PathBuf::from(s)) else { return true };
    md.uid() == unsafe { libc::geteuid() }
}

Try / catch

if !lock_owned_by_me(&sock_path) {
    anyhow::bail!("stale foreign-owned lock; rm it as its owner or root");
}

Prevention

When it happens

Trigger: A previous daemon run under a different user account (root, then switched to a service user) left the lock behind; a shared socket path where another user's daemon created the lock first; restoring a state directory from a tarball that changed ownership.

Common situations: Running once with sudo then as the service user (or vice versa); uid changes after user deletion/recreation; shared machines where two users configured the same ZEROCLAW_SOCKET.

Related errors


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