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

runtime.shell {shell:?} (resolved to {}) is not a regular fi

Error message

runtime.shell {shell:?} (resolved to {}) is not a regular file

What it means

validate_shell requires the resolved shell path to be a regular file. Directories, fifos, sockets, and device nodes all parse as paths but cannot be exec'd as a program, so they are rejected before spawn.

Source

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

        }
    };

    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!(
            "runtime.shell {shell:?} (resolved to {}) is not executable",
            resolved.display()
        );
    }

    Ok(())

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Point at the shell binary itself, not its directory.
  2. Inspect what the path really is: ls -l and file <path>; fix or remove symlinks that target a non-file.
  3. If the shell package's files are corrupted, reinstall it.

Example fix

# before
[runtime]
shell = "/usr/bin"   # a directory

# after
[runtime]
shell = "/usr/bin/bash"
Defensive patterns

Strategy: type-guard

Validate before calling

if !is_regular_file(&resolved_shell_path) {
    return Err(anyhow::anyhow!("shell path is not a regular file"));
}

Type guard

fn is_regular_file(p: &std::path::Path) -> bool {
    std::fs::metadata(p).map(|m| m.is_file()).unwrap_or(false)
}

Try / catch

match create_runtime(&config) {
    Err(e) if e.to_string().contains("not a regular file") => {
        // path points at a dir/fifo/socket; point at the binary itself
    }
    other => other,
}

Prevention

When it happens

Trigger: runtime.shell resolving to a directory ("/usr/bin" instead of "/usr/bin/bash"), a symlink whose target is a fifo or socket, or a device node.

Common situations: Typos dropping the binary name and leaving the trailing slash or bare directory; tool-managed symlinks that replaced the binary with something odd; minimal container images with an unusual /bin.

Related errors


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