BoundaryML/baml · critical

failed to exec {}: {err}{origin}

Error message

failed to exec {}: {err}{origin}

What it means

After `pass_through` resolves a toolchain-specific CLI binary (`cli` path) and attributes it (`origin`), it execs it on Unix. If the exec syscall fails — the binary is missing, not executable, has a broken interpreter/shebang, or lives on a noexec mount — this error is thrown with the binary path, the io::Error, and the origin annotation identifying which toolchain provided it.

Source

Thrown at baml_language/crates/baml/src/main.rs:498

    verify_path_toolchain(cli, &origin)?;
    reject_self_exec(cli, &origin)?;

    let mut command = Command::new(cli);
    command.args(args);
    command.env("BAML_WRAPPER_EXEC", "1");
    // Deliberately not BAML_WRAPPER_RESOLVED_TOOLCHAIN: that carries a version,
    // and a local build has none. A separate variable also lets the toolchain
    // binary tell the two situations apart, which `baml ide install` needs.
    command.env("BAML_WRAPPER_LOCAL_TOOLCHAIN", cli);

    // Anything verify_path_toolchain could not rule out (wrong architecture,
    // a noexec mount, a missing interpreter) surfaces here, so this message
    // carries the attribution too.
    #[cfg(unix)]
    {
        use std::os::unix::process::CommandExt;
        let err = command.exec();
        Err(anyhow!("failed to exec {}: {err}{origin}", cli.display()))
    }
    #[cfg(not(unix))]
    {
        let status = command
            .status()
            .map_err(|err| anyhow!("failed to run {}: {err}{origin}", cli.display()))?;
        Ok(status.code().unwrap_or(1))
    }
}

fn active_selector() -> Result<ResolvedSelector> {
    if let Ok(value) = env::var("BAML_VERSION") {
        if !value.trim().is_empty() {
            return Ok(ResolvedSelector {
                selector: normalize_selector(value.trim(), &env::current_dir()?),
                source: SelectorSource::Env,
            });
        }

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Read the `{origin}` suffix to identify which toolchain binary failed, then verify it exists and is executable: `ls -l <path>`.
  2. Reinstall the offending toolchain: `baml toolchain install <version> --force`.
  3. Switch to a known-good toolchain: `baml toolchain use canary` or a previously working version.
  4. Check the filesystem mount for noexec and the binary architecture with `file <path>`.
  5. Inspect the wrapped io::Error (ENOENT/EACCES/ENOEXEC) to pinpoint whether the path, permissions, or format is the problem.

Example fix

# before (pinned toolchain binary deleted)
$ baml generate
error: failed to exec /home/u/.baml/toolchains/0.199.0/bin/baml-cli: No such file or directory (os error 2) (pinned via toolchain manifest)

# after
$ baml toolchain install 0.199.0 --force
$ baml generate
Defensive patterns

Strategy: try-catch

Validate before calling

import os, stat, subprocess
def check_toolchain_binary():
    out = subprocess.run(["baml", "toolchain", "list"], capture_output=True, text=True)
    # confirm the active/pinned toolchain path exists and is executable before dispatch
    for line in out.stdout.splitlines():
        if line.startswith("*") and "/bin/baml-cli" in line:
            p = line.split()[-1]
            if not os.path.exists(p):
                raise SystemExit(f"active toolchain binary missing: {p} — reinstall with `baml toolchain install --force`")
            if not (os.stat(p).st_mode & stat.S_IXUSR):
                raise SystemExit(f"not executable: {p}")

Type guard

function canExec(p: string): boolean {
  try { fs.accessSync(p, fs.constants.X_OK); return true; } catch { return false; }
}

Try / catch

result = subprocess.run(["baml"] + args, capture_output=True)
if result.returncode != 0 and "failed to exec" in result.stderr.decode():
    msg = result.stderr.decode()
    origin = msg.split("(")[-1]  # attribution of which toolchain supplied the binary
    print(f"Toolchain binary unusable ({origin}); switching to known-good and reinstalling")
    subprocess.run(["baml", "toolchain", "use", "canary"], check=True)

Prevention

When it happens

Trigger: Running any `baml` command that dispatches into an active toolchain binary where exec fails: toolchain deleted/moved after pinning, exec bit stripped, corrupted download, incompatible binary for the CPU/OS, or noexec mount.

Common situations: Pinning a toolchain path that was later removed, partial downloads from a custom `--manifest-base-url` mirror, cross-architecture toolchain installs (e.g. x86_64 binary on Apple Silicon without Rosetta), noexec NAS/home mounts, or hardlink/symlink targets deleted.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/1c2037ecdd73ba40. Report an issue: GitHub.