Hmbown/CodeWhale · error · anyhow::Error

no trusted executable search path remains outside the worksp

Error message

no trusted executable search path remains outside the workspace

What it means

For read-only execution, PATH is sanitized: relative entries, entries that fail to canonicalize, and entries resolving inside the workspace are all dropped — this is what stops workspace-planted binaries from shadowing system tools (see the shadow test at crates/tui/src/tools/shell/tests.rs:801). This error means nothing survived the filter, so there is no trusted location left to resolve any program from, and the command is refused.

Source

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

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) {
        vec![format!("{program}.exe"), format!("{program}.com")]
    } else {
        vec![program.to_string()]
    };
    for directory in std::env::split_paths(&safe_path) {
        for name in &names {
            let candidate = directory.join(name);
            if !candidate.is_file() {
                continue;
            }
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt as _;
                if candidate.metadata()?.permissions().mode() & 0o111 == 0 {
                    continue;
                }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Prepend standard system directories (/usr/bin, /usr/local/bin, /bin) to PATH outside the workspace
  2. Remove broken or relative PATH entries so the filter keeps at least one trusted directory
  3. Install the allowlisted tools (git, gh, rg) system-wide rather than only inside the workspace
  4. Validate the sanitized PATH at startup and report the environment problem early

Example fix

# before: PATH only covers the workspace
PATH=$PWD/node_modules/.bin codewhale tui
# after: system dirs first so a trusted path survives sanitization
PATH=/usr/local/bin:/usr/bin:/bin:$PWD/node_modules/.bin codewhale tui
Defensive patterns

Strategy: validation

Validate before calling

fn sanitized_path_survives(workspace: &std::path::Path) -> bool {
    let Some(path) = std::env::var_os("PATH") else { return false };
    std::env::split_paths(&path).any(|entry| {
        entry.is_absolute()
            && entry
                .canonicalize()
                .map(|resolved| !resolved.starts_with(workspace))
                .unwrap_or(false)
    })
}

if !sanitized_path_survives(&workspace) {
    return report("PATH has no trusted directories outside the workspace; add /usr/bin:/bin");
}

Type guard

fn trusted_path_available(workspace: &std::path::Path) -> bool {
    std::env::var_os("PATH").is_some_and(|path| {
        std::env::split_paths(&path).any(|entry| {
            entry.is_absolute()
                && entry
                    .canonicalize()
                    .map(|resolved| !resolved.starts_with(workspace))
                    .unwrap_or(false)
        })
    })
}

Try / catch

if let Err(err) = run_readonly_command(&command) {
    if err.to_string().contains("no trusted executable search path remains") {
        return report("prepend system bin dirs (/usr/bin:/usr/local/bin:/bin) to PATH outside the workspace and retry");
    }
    return Err(err);
}

Prevention

When it happens

Trigger: PATH containing only the workspace directory (or only relative/broken entries): devcontainer or Nix setups that put just project-local bin dirs on PATH; scrubbed environments whose sole entry is the repo; entries pointing at deleted directories plus one workspace entry.

Common situations: Project-local toolchains that replace PATH entirely; sandbox configs that reduce PATH to the repo; misconstructed test environments.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/1f453ff41934b3d0. Report an issue: GitHub.