zeroclaw-labs/zeroclaw · error · anyhow::Error

runtime.shell {shell:?} (resolved to {}) could not be inspec

Error message

runtime.shell {shell:?} (resolved to {}) could not be inspected: {e}

What it means

validate_shell calls metadata() on the resolved path and surfaces the IO error verbatim when the stat itself fails. Unlike the not-exists case, the path is present but cannot be inspected by this process — EACCES on the file or a parent directory is the classic cause.

Source

Thrown at crates/zeroclaw-config/src/platform/mod.rs:69

            .find(|candidate| candidate.is_file())
        {
            Some(found) => found,
            None => anyhow::bail!(
                "runtime.shell {shell:?} was not found on PATH; use an absolute path or install the shell"
            ),
        }
    };

    if !resolved.exists() {
        anyhow::bail!(
            "runtime.shell {shell:?} (resolved to {}) does not exist",
            resolved.display()
        );
    }

    let metadata = match resolved.metadata() {
        Ok(metadata) => metadata,
        Err(e) => anyhow::bail!(
            "runtime.shell {shell:?} (resolved to {}) could not be inspected: {e}",
            resolved.display()
        ),
    };
    if !metadata.is_file() {
        anyhow::bail!(
            "runtime.shell {shell:?} (resolved to {}) is not a regular file",
            resolved.display()
        );
    }

    // Coarse check: reject only when no execute bit is set at all. A precise
    // "can *we* execute it" test (uid/gid vs. the file owner) buys little —
    // the kernel's spawn is the real authority (ACLs, caps, mount flags) — and
    // this is a fail-fast sanity check, not a security gate.
    let mode = metadata.permissions().mode();
    if mode & 0o111 == 0 {
        anyhow::bail!(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Trace where traversal fails: namei -l /path/to/shell, then chmod a+rx the offending directories.
  2. Run zeroclaw as a user that can read the path, or move the shell to a system location like /usr/local/bin.
  3. If the message reports an I/O error rather than permission denied, fix the underlying filesystem or mount problem.

Example fix

# before: stat fails with EACCES on a parent directory
$ namei -l /opt/private/sh   # shows 'drwx------' on /opt/private

# after
$ chmod a+rx /opt/private    # or move the shell to /usr/local/bin
Defensive patterns

Strategy: try-catch

Validate before calling

if let Err(e) = std::fs::metadata(resolved_shell_path) {
    // surface a friendly message: check parent-dir perms (namei -l), user, mount health
    return Err(anyhow::anyhow!("cannot inspect shell {path}: {e}"));
}

Try / catch

match create_runtime(&config) {
    Err(e) if e.to_string().contains("could not be inspected") => {
        // inspect errno: PermissionDenied → fix dir perms; other → fix the filesystem/mount
    }
    other => other,
}

Prevention

When it happens

Trigger: The resolved shell path's file or one of its parent directories lacks read/traverse permission for the zeroclaw user, so metadata() returns an error; also races (file deleted mid-check) or filesystem-level failures (NFS stall, I/O error).

Common situations: Running zeroclaw as a service account different from the shell's owner; homebrew or toolchain directories with restrictive permissions; network filesystems; containers with mismatched uid mapping.

Related errors


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