zeroclaw-labs/zeroclaw · error

local IPC endpoint lock directory {} is writable by other us

Error message

local IPC endpoint lock directory {} is writable by other users without the sticky bit; its entries could be replaced. Restrict it (chmod go-w or +t) or point ZEROCLAW_SOCKET at a private directory

What it means

The lock directory is group- or other-writable (mode bits 0o022 set) and lacks the sticky bit (0o1000). Any local user could then replace the lock entry and sabotage or spoof endpoint lifecycle ownership, so ZeroClaw refuses to acquire the lock. The sticky bit keeps /tmp-style shared directories usable because it restricts unlink/rename to the entry owner.

Source

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

        };
        let metadata = std::fs::metadata(parent).with_context(|| {
            format!(
                "inspecting local IPC endpoint lock directory {}",
                parent.display()
            )
        })?;
        let euid = unsafe { libc::geteuid() };
        let mode = metadata.mode();
        if metadata.uid() != euid && metadata.uid() != 0 {
            anyhow::bail!(
                "local IPC endpoint lock directory {} is owned by uid {}; \
                 it must belong to the daemon user or root",
                parent.display(),
                metadata.uid()
            );
        }
        if mode & 0o022 != 0 && mode & 0o1000 == 0 {
            anyhow::bail!(
                "local IPC endpoint lock directory {} is writable by other \
                 users without the sticky bit; its entries could be replaced. \
                 Restrict it (chmod go-w or +t) or point ZEROCLAW_SOCKET at a \
                 private directory",
                parent.display()
            );
        }
        Ok(())
    }

    /// 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.

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Restrict the directory: chmod go-w /path/to/dir (removes group/other write).
  2. Or set the sticky bit: chmod +t /path/to/dir.
  3. Or move the socket to a private directory owned by the daemon user and update ZEROCLAW_SOCKET.
  4. Audit whoever created the directory with permissive modes so restarts don't regress.

Example fix

# before
export ZEROCLAW_SOCKET=/tmp/zeroclaw.sock   # /tmp mode 1777 is fine, but a custom /tmp/zeroclaw dir with 0777 is not
chmod 0777 /tmp/zeroclaw

# after
chmod go-w /tmp/zeroclaw        # or: chmod +t /tmp/zeroclaw
# better: private dir
mkdir -p "$XDG_RUNTIME_DIR/zeroclaw"
export ZEROCLAW_SOCKET="$XDG_RUNTIME_DIR/zeroclaw/zeroclaw.sock"
Defensive patterns

Strategy: validation

Validate before calling

fn dir_not_foreign_writable(sock: &std::path::Path) -> bool {
    let dir = sock.parent().unwrap_or(std::path::Path::new("."));
    std::fs::metadata(dir)
        .map(|m| m.mode() & 0o022 == 0 || m.mode() & 0o1000 != 0)
        .unwrap_or(false)
}

Try / catch

if !dir_not_foreign_writable(&sock_path) {
    anyhow::bail!("socket dir is foreign-writable; chmod go-w or +t first");
}

Prevention

When it happens

Trigger: ZEROCLAW_SOCKET placed directly in /tmp or another world-writable directory without +t; a group-writable directory (mode 0775/0777) shared by several service accounts; a container image that creates the socket dir with permissive defaults.

Common situations: Quick local setups pointing the socket at /tmp; directories created by Dockerfiles with chmod 777; legacy shared /var/run dirs with group write for multi-user tooling.

Related errors


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