rust-lang/cargo · error

output must exist after running

Error message

output must exist after running

What it means

After a build script runs, the fingerprint job reads its captured stdout via `build_script_outputs.get(metadata).expect("output must exist after running")`. Cargo inserts the build script's parsed output into the shared `build_script_outputs` map at the moment it finishes; the fingerprint job runs only after that insert. The panic means the metadata key was absent — the script's output was never recorded, was overwritten/removed, or a different metadata value was used.

Source

Thrown at src/compiler/fingerprint/mod.rs:568

        // while we're executing it. For example it could be in the legacy
        // "consider everything a dependency mode" and then we switch to "deps
        // are explicitly specified" mode.
        //
        // To handle this movement we need to regenerate the `local` field of a
        // build script's fingerprint after it's executed. We do this by
        // using the `build_script_local_fingerprints` function which returns a
        // thunk we can invoke on a foreign thread to calculate this.
        let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
        let metadata = build_runner.get_run_build_script_metadata(unit);
        let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?;
        let output_path = build_runner.build_explicit_deps[unit]
            .build_script_output
            .clone();
        Work::new(move |_| {
            let outputs = build_script_outputs.lock().unwrap();
            let output = outputs
                .get(metadata)
                .expect("output must exist after running");
            let deps = BuildDeps::new(&output_path, Some(output));

            // FIXME: it's basically buggy that we pass `None` to `call_box`
            // here. See documentation on `build_script_local_fingerprints`
            // below for more information. Despite this just try to proceed and
            // hobble along if it happens to return `Some`.
            if let Some(new_local) = (gen_local)(&deps, None)? {
                *fingerprint.local.lock().unwrap() = new_local;
            }

            write_fingerprint(&loc, &fingerprint)
        })
    } else {
        Work::new(move |_| write_fingerprint(&loc, &fingerprint))
    };

    Ok(Job::new_dirty(write_fingerprint, dirty_reason))
}

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Re-run the build; transient kills/timing races usually clear on retry.
  2. Inspect whether the build script itself failed (`cargo build -vv`) — fix the script first.
  3. `cargo clean` to remove stale state, then rebuild.
  4. Avoid running multiple Cargo invocations against the same `target/` simultaneously.

Example fix

// before
let output = outputs
    .get(metadata)
    .expect("output must exist after running");
// after
let output = outputs.get(metadata).ok_or_else(|| {
    anyhow::anyhow!("build script output for metadata `{:?}` was not recorded; did the script fail to run?", metadata)
})?;
Defensive patterns

Strategy: retry

Validate before calling

// Before the build, sanity-check that build scripts in your dependency tree succeed standalone:
// `cargo build -vv 2>&1 | grep 'error'` to surface build-script failures early.

Try / catch

// Retry the build once; transient races usually clear
for attempt in 0..2 {
    let s = std::process::Command::new("cargo").arg("build").status()?;
    if s.success() { break; }
    if attempt == 1 { return Err(s.into()); }
    std::thread::sleep(std::time::Duration::from_secs(1));
}

Prevention

When it happens

Trigger: A build script panic/error that prevented its output from being inserted yet still scheduled the fingerprint job; the metadata object compared by a key that mutated between insert and lookup; concurrent cleanup wiping the map; Cargo bug in dependency-metadata passing for foreign-thread fingerprint calculation.

Common situations: Build script that exits non-zero or is killed mid-run; concurrent `cargo clean`; long-lived Cargo-as-library process whose `build_script_outputs` was cleared; thread-safety regression in the shared `Mutex<HashMap>`.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/ba1cd685f4b024c3.json. Report an issue: GitHub.