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
- Prepend standard system directories (/usr/bin, /usr/local/bin, /bin) to PATH outside the workspace
- Remove broken or relative PATH entries so the filter keeps at least one trusted directory
- Install the allowlisted tools (git, gh, rg) system-wide rather than only inside the workspace
- 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
- Keep standard system directories on PATH ahead of project-local entries
- Remove broken/relative PATH entries so canonicalize does not drop them
- Install allowlisted tools (git, gh, rg) outside the workspace, system-wide
- Validate the sanitized PATH at startup — the filter deliberately discards workspace entries to stop shadow executables
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
- no executable search path is configured
- could not parse classifier-approved read command: {error}
- classifier-approved read command was empty
- classifier-approved Git read was missing its literal subcomm
- classifier-approved Git read did not keep its subcommand in
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/1f453ff41934b3d0.
Report an issue: GitHub.