rust-lang/cargo · error · anyhow::Error

$CARGO not set

Error message

$CARGO not set

What it means

Thrown inside GlobalContext::cargo_exe() by the from_env closure when the $CARGO environment variable is absent. Cargo uses a three-tier fallback chain to locate its own binary: $CARGO first, then env::current_exe(), then argv[0]. This specific error text is produced by the first tier but is only surfaced to the user when all three tiers fail, at which point it is wrapped with the context 'couldn't get the path to cargo executable'.

Solutions

  1. Set the CARGO environment variable to the absolute path of the cargo binary before invoking the process: `export CARGO=$(which cargo)`
  2. If running inside a container or chroot, ensure /proc is mounted so env::current_exe() works as a fallback
  3. If calling Cargo as a library, set CARGO_ENV explicitly via std::env::set_var("CARGO", &cargo_path) before calling cargo_exe()

Example fix

// before: CARGO not set, current_exe is not 'cargo'
let gctx = GlobalContext::default();
let cargo = gctx.cargo_exe()?; // fails

// after: set CARGO before building context
std::env::set_var("CARGO", "/usr/local/bin/cargo");
let gctx = GlobalContext::default();
let cargo = gctx.cargo_exe()?;
Defensive patterns

Strategy: validation

Validate before calling

// Validate before calling cargo_exe()
fn ensure_cargo_env() -> Result<(), anyhow::Error> {
    if std::env::var_os("CARGO").is_none()
        && std::env::current_exe()
            .ok()
            .and_then(|p| p.file_stem().map(|s| s == std::ffi::OsStr::new("cargo")))
            != Some(true)
    {
        let cargo = which::which("cargo")
            .map_err(|_| anyhow::anyhow!("cargo not found in PATH and $CARGO not set"))?;
        std::env::set_var("CARGO", cargo);
    }
    Ok(())
}

Try / catch

match gctx.cargo_exe() {
    Ok(path) => { /* use path */ }
    Err(e) => {
        // Fallback: search PATH manually
        let cargo = which::which("cargo")
            .expect("cargo not found in PATH and $CARGO not set");
        std::env::set_var("CARGO", &cargo);
    }
}

Prevention

When it happens

Trigger: Calling GlobalContext::cargo_exe() (or any internal Cargo operation that needs the cargo binary path) in a context where $CARGO is unset, env::current_exe() either fails or does not resolve to a binary named 'cargo', and argv[0] also does not resolve to cargo.

Common situations: Running Cargo as a library (cargo-* subcommand, Valgrind, ld.so) without exporting $CARGO. Containers or chroots where /proc is not mounted so env::current_exe() fails. Executing through a wrapper that strips environment variables.

Related errors


AI-assisted analysis of rust-lang/cargo@98a09e7e7d (2026-08-11). Data as JSON: /api/errors/f5c6af20b69d89a6. Report an issue: GitHub.

Appendix: source

Thrown at src/context/mod.rs:560

                None
            },
            self,
        )
    }

    /// Gets the path to the `cargo` executable.
    pub fn cargo_exe(&self) -> CargoResult<&Path> {
        self.cargo_exe
            .try_borrow_with(|| {
                let from_env = || -> CargoResult<PathBuf> {
                    // Try re-using the `cargo` set in the environment already. This allows
                    // commands that use Cargo as a library to inherit (via `cargo <subcommand>`)
                    // or set (by setting `$CARGO`) a correct path to `cargo` when the current exe
                    // is not actually cargo (e.g., `cargo-*` binaries, Valgrind, `ld.so`, etc.).
                    let exe = self
                        .get_env_os(crate::CARGO_ENV)
                        .map(PathBuf::from)
                        .ok_or_else(|| anyhow!("$CARGO not set"))?;
                    Ok(exe)
                };

                fn from_current_exe() -> CargoResult<PathBuf> {
                    // Try fetching the path to `cargo` using `env::current_exe()`.
                    // The method varies per operating system and might fail; in particular,
                    // it depends on `/proc` being mounted on Linux, and some environments
                    // (like containers or chroots) may not have that available.
                    let exe = env::current_exe()?;
                    Ok(exe)
                }

                fn from_argv() -> CargoResult<PathBuf> {
                    // Grab `argv[0]` and attempt to resolve it to an absolute path.
                    // If `argv[0]` has one component, it must have come from a `PATH` lookup,
                    // so probe `PATH` in that case.
                    // Otherwise, it has multiple components and is either:
                    // - a relative path (e.g., `./cargo`, `target/debug/cargo`), or

View on GitHub (pinned to 98a09e7e7d)