{"id":"ba1cd685f4b024c3","repo":"rust-lang/cargo","slug":"output-must-exist-after-running","errorCode":null,"errorMessage":"output must exist after running","messagePattern":"output must exist after running","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/compiler/fingerprint/mod.rs","lineNumber":568,"sourceCode":"        // while we're executing it. For example it could be in the legacy\n        // \"consider everything a dependency mode\" and then we switch to \"deps\n        // are explicitly specified\" mode.\n        //\n        // To handle this movement we need to regenerate the `local` field of a\n        // build script's fingerprint after it's executed. We do this by\n        // using the `build_script_local_fingerprints` function which returns a\n        // thunk we can invoke on a foreign thread to calculate this.\n        let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);\n        let metadata = build_runner.get_run_build_script_metadata(unit);\n        let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?;\n        let output_path = build_runner.build_explicit_deps[unit]\n            .build_script_output\n            .clone();\n        Work::new(move |_| {\n            let outputs = build_script_outputs.lock().unwrap();\n            let output = outputs\n                .get(metadata)\n                .expect(\"output must exist after running\");\n            let deps = BuildDeps::new(&output_path, Some(output));\n\n            // FIXME: it's basically buggy that we pass `None` to `call_box`\n            // here. See documentation on `build_script_local_fingerprints`\n            // below for more information. Despite this just try to proceed and\n            // hobble along if it happens to return `Some`.\n            if let Some(new_local) = (gen_local)(&deps, None)? {\n                *fingerprint.local.lock().unwrap() = new_local;\n            }\n\n            write_fingerprint(&loc, &fingerprint)\n        })\n    } else {\n        Work::new(move |_| write_fingerprint(&loc, &fingerprint))\n    };\n\n    Ok(Job::new_dirty(write_fingerprint, dirty_reason))\n}","sourceCodeStart":550,"sourceCodeEnd":586,"githubUrl":"https://github.com/rust-lang/cargo/blob/0e07a155371a6ce88ae53a2c00df940280c09a67/src/compiler/fingerprint/mod.rs#L550-L586","documentation":"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.","triggerScenarios":"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.","commonSituations":"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>`.","solutions":["Re-run the build; transient kills/timing races usually clear on retry.","Inspect whether the build script itself failed (`cargo build -vv`) — fix the script first.","`cargo clean` to remove stale state, then rebuild.","Avoid running multiple Cargo invocations against the same `target/` simultaneously."],"exampleFix":"// before\nlet output = outputs\n    .get(metadata)\n    .expect(\"output must exist after running\");\n// after\nlet output = outputs.get(metadata).ok_or_else(|| {\n    anyhow::anyhow!(\"build script output for metadata `{:?}` was not recorded; did the script fail to run?\", metadata)\n})?;","handlingStrategy":"retry","validationCode":"// Before the build, sanity-check that build scripts in your dependency tree succeed standalone:\n// `cargo build -vv 2>&1 | grep 'error'` to surface build-script failures early.","typeGuard":null,"tryCatchPattern":"// Retry the build once; transient races usually clear\nfor attempt in 0..2 {\n    let s = std::process::Command::new(\"cargo\").arg(\"build\").status()?;\n    if s.success() { break; }\n    if attempt == 1 { return Err(s.into()); }\n    std::thread::sleep(std::time::Duration::from_secs(1));\n}","preventionTips":["Fix build scripts that exit non-zero before relying on their outputs.","Avoid running multiple Cargo builds against the same `target/` simultaneously.","Don't `cargo clean` while a build is in progress."],"tags":["cargo","build-scripts","fingerprint","concurrency","internal-invariant"],"analyzedSha":"0e07a155371a6ce88ae53a2c00df940280c09a67","analyzedAt":"2026-08-06T01:46:58.334Z","schemaVersion":2}