Hmbown/CodeWhale · error

no executable search path is configured

Error message

no executable search path is configured

What it means

`resolve_readonly_program` reads the `PATH` environment variable to search for an allowlisted read-only executable. If `PATH` is absent from the process environment, `std::env::var_os("PATH")` returns None and this error is thrown, because the resolver cannot enumerate candidate directories for the program.

Solutions

  1. Ensure `PATH` is set in the environment launching the process, e.g. `env PATH=/usr/bin:/bin <app>`
  2. In Rust parents, use `Command::env("PATH", ...)` rather than `env_clear()` without re-adding PATH
  3. In systemd/cron, add `Environment="PATH=/usr/local/bin:/usr/bin:/bin"` to the unit or job

Example fix

// before (parent Rust process)
Command::new(app).env_clear().status()
// after
Command::new(app).env_clear().env("PATH", "/usr/local/bin:/usr/bin:/bin").status()
Defensive patterns

Strategy: validation

Validate before calling

fn path_configured() -> bool {
    std::env::var_os("PATH").is_some_and(|p| !p.is_empty())
}
assert!(path_configured(), "PATH must be set before running read-only commands");

Prevention

When it happens

Trigger: Executing a read-only shell command (e.g. `git status`, `rg pattern`) in a process whose environment has no `PATH` variable at all — distinct from an empty PATH, which simply yields no candidates.

Common situations: Spawning the app with a scrubbed/empty environment (`env -i`, `Command::env_clear()` in a parent); container or CI images that never set PATH for the service user; systemd units or cron jobs with minimal `Environment=` blocks.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/51fd15b6a25e7f8e. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/shell.rs:4380

    let workspace = workspace.canonicalize().ok()?;
    let safe = std::env::split_paths(path).filter_map(|entry| {
        if !entry.is_absolute() {
            return None;
        }
        let resolved = entry.canonicalize().ok()?;
        (!resolved.starts_with(&workspace)).then_some(resolved)
    });
    std::env::join_paths(safe).ok()
}

fn readonly_sanitized_path(workspace: &std::path::Path) -> Option<String> {
    let path = std::env::var_os("PATH")?;
    readonly_sanitized_path_from(workspace, &path).map(|value| value.to_string_lossy().into_owned())
}

fn resolve_readonly_program(program: &str, workspace: &std::path::Path) -> Result<PathBuf> {
    let path = std::env::var_os("PATH")
        .ok_or_else(|| anyhow!("no executable search path is configured"))?;
    resolve_readonly_program_from_path(program, workspace, &path)
}

fn resolve_readonly_program_from_path(
    program: &str,
    workspace: &std::path::Path,
    path: &std::ffi::OsStr,
) -> Result<PathBuf> {
    let workspace = workspace.canonicalize()?;
    if std::path::Path::new(program).components().count() != 1 {
        return Err(anyhow!(
            "read-only command must name a bare allowlisted executable"
        ));
    }
    let safe_path = readonly_sanitized_path_from(&workspace, path).ok_or_else(|| {
        anyhow!("no trusted executable search path remains outside the workspace")
    })?;
    let names = if cfg!(windows) {

View on GitHub (pinned to 73e0f67d83)