Hmbown/CodeWhale · error · anyhow::Error
read-only command must name a bare allowlisted executable
Error message
read-only command must name a bare allowlisted executable
What it means
Read-only dispatch requires the program token to be a bare executable name — exactly one path component (shell.rs:3893) — so the resolver controls lookup and can exclude the workspace from the search path. Any token containing a separator or parent component (`./git`, `bin/ls`, `/usr/bin/git`, `../rg`) is refused before resolution starts; this is deliberate, because a caller-chosen path would bypass the anti-shadowing resolution entirely.
Source
Thrown at crates/tui/src/tools/shell.rs:3894
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) {
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)]View on GitHub (pinned to 0c42157ee5)
Solutions
- Use the bare program name: `git log`, not `/usr/bin/git log`
- Ensure the needed tool is installed on PATH outside the workspace (system-wide)
- For workspace-local tools, use a full-permission shell call with approval instead of the read-only path
- Lint generated commands for path-qualified program tokens before dispatch
Example fix
# before: path-qualified program token /usr/bin/git log --oneline # after: bare name resolved through the sanitized PATH git log --oneline
Defensive patterns
Strategy: validation
Validate before calling
fn is_bare_program(token: &str) -> bool {
std::path::Path::new(token).components().count() == 1
}
let mut argv = shell_words::split(command)?;
if !is_bare_program(&argv[0]) {
return report(format!("use a bare program name, not {token:?}", token = argv[0]));
} Type guard
fn bare_program_token(command: &str) -> bool {
shell_words::split(command)
.ok()
.and_then(|argv| argv.first().cloned())
.is_some_and(|program| std::path::Path::new(&program).components().count() == 1)
} Try / catch
if let Err(err) = run_readonly_command(&command) {
if err.to_string().contains("bare allowlisted executable") {
return report("strip the path prefix from the program and retry with a bare name");
}
return Err(err);
} Prevention
- Emit bare program names (`git`, `rg`) — never absolute or `./`-relative tool paths
- Block path-qualified program tokens in command generation/linting
- Install required tools system-wide so bare names resolve
- Remember path-qualified programs would bypass the anti-shadowing PATH filter — the refusal is intentional
When it happens
Trigger: A classifier-admitted read command whose program token is path-qualified, e.g. `/usr/bin/git log` or `./rg pattern`, reaching `resolve_readonly_program_from_path` on the readonly exec branch.
Common situations: Models emitting absolute tool paths to be 'explicit'; wrappers that prefix `./` for repo-local binaries; hardened environments where users alias tools via paths.
Related errors
- classifier-approved Git read did not keep its subcommand in
- could not parse classifier-approved read command: {error}
- classifier-approved read command was empty
- classifier-approved Git read was missing its literal subcomm
- no trusted executable search path remains outside the worksp
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/404de4f8cace90d8.
Report an issue: GitHub.