xai-org/grok-build · error
failed to exec {program}: {err}
Error message
failed to exec {program}: {err} What it means
On Unix, `exec_command` uses `std::os::unix::process::CommandExt::exec`, which replaces the current process and only ever returns if the program could not be executed (not found, permission denied, exec format error). The library wraps that errno error in an anyhow error naming the program. It means the wrapper could not hand off to the target binary at all.
Source
Thrown at crates/codegen/xai-grok-pager/src/wrap_cmd.rs:199
if !cfg!(any(unix, windows)) {
return false;
}
use std::io::IsTerminal;
std::io::stdin().is_terminal()
&& std::io::stdout().is_terminal()
&& std::io::stderr().is_terminal()
}
/// Replace the current process with `program <args...>` (no PTY wrapping).
#[cfg(unix)]
fn exec_command(program: &str, args: &[String]) -> Result<()> {
use std::os::unix::process::CommandExt;
let err = std::process::Command::new(program).args(args).exec();
// exec() only returns on error.
Err(anyhow::anyhow!("failed to exec {program}: {err}"))
}
/// On non-Unix platforms, spawn and wait.
#[cfg(not(unix))]
fn exec_command(program: &str, args: &[String]) -> Result<()> {
let status = std::process::Command::new(program)
.args(args)
.status()
.map_err(|e| anyhow::anyhow!("failed to run {program}: {e}"))?;
std::process::exit(status.code().unwrap_or(1));
}
#[cfg(all(test, unix))]
#[path = "wrap_cmd_tests.rs"]
mod tests;
View on GitHub (pinned to bc7f02eddd)
Solutions
- Run `which <program>` in the same environment to confirm the binary resolves on PATH.
- Check the execute permission bit (`ls -l`, `chmod +x`) on the target binary.
- Verify the binary matches the platform architecture (`file <program>`).
- If invoked with a relative path, use an absolute path or resolve it programmatically before exec.
- Inspect the inner errno in `{err}` (NotFound vs PermissionDenied vs ExecFormatError) to pinpoint the cause.
Example fix
// before
exec_command("grok-sandboxed-tool", &args)?; // relies on PATH inside sandbox
// after
let program = which::which("grok-sandboxed-tool")
.map_err(|_| anyhow::anyhow!("grok-sandboxed-tool not found on PATH"))?
.to_string_lossy().to_string();
exec_command(&program, &args)?; Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn can_exec(program: &str) -> Result<(), String> {
if program.contains('/') {
let p = Path::new(program);
if !p.exists() { return Err(format!("{program} does not exist")); }
return std::fs::metadata(p)
.map(|m| m.permissions().mode() & 0o111 != 0)
.map_err(|e| e.to_string())
.and_then(|x| if x { Ok(()) } else { Err(format!("{program} not executable")) });
}
let path = std::env::var("PATH").unwrap_or_default();
for dir in path.split(':') {
let candidate = Path::new(dir).join(program);
if candidate.is_file() { return Ok(()); }
}
Err(format!("{program} not found on PATH"))
} Try / catch
if let Err(e) = exec_command(program, &args) {
let msg = e.to_string();
if msg.contains("No such file") {
eprintln!("program '{program}' not found; check PATH");
} else if msg.contains("Permission denied") {
eprintln!("program '{program}' is not executable; chmod +x");
} else {
eprintln!("exec failed: {e:#}");
}
std::process::exit(127);
} Prevention
- Resolve program paths explicitly (which/absolute path) instead of relying on PATH inside sandboxes.
- Check execute permissions after copying or installing binaries.
- Verify binary architecture matches the host (`file <program>`).
- Keep PATH stable and documented for wrapper entry points.
When it happens
Trigger: The wrapped program name does not exist on PATH; the file exists but lacks the execute bit; the binary is for a different architecture (ENOEXEC); a shebang points at a missing interpreter.
Common situations: PATH altered inside a sandboxed wrapper so the target binary is invisible; user moved/renamed the tool the wrapper delegates to; running a macOS binary on Linux; node_modules bin scripts losing +x after a bad copy.
Related errors
- hook JSON alias validation failed: {e}
- refusing degenerate process-group id {pid} (0 = own group, 1
- process-group id {pid} exceeds i32::MAX; cannot be used with
- refusing to killpg the caller's own process group ({pid})
- git {shown} failed in {}: {}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/8b619aa9261fd762.
Report an issue: GitHub.