rust-lang/rust · critical
Could not find work-product for CGU `{}`
Error message
Could not find work-product for CGU `{}` What it means
`CodegenUnit::previous_work_product` (mono.rs:521) does a `.unwrap_or_else(|| panic!(...))` on the previous dep-graph's work product for this CGU's `WorkProductId`. The lookup should always succeed when incremental compilation is consistent: every CGU recorded in the current partition must have a matching entry produced in the prior session. A miss means the incremental dep graph and the current CGU set disagree—typically a stale or partially-overwritten `target/incremental` directory.
Source
Thrown at compiler/rustc_middle/src/mono.rs:521
// Items are never zero-sized, so if we have items the estimate must be
// non-zero, unless we forgot to call `compute_size_estimate` first.
assert!(self.items.is_empty() || self.size_estimate != 0);
self.size_estimate
}
pub fn contains_item(&self, item: &MonoItem<'tcx>) -> bool {
self.items().contains_key(item)
}
pub fn work_product_id(&self) -> WorkProductId {
WorkProductId::from_cgu_name(self.name().as_str())
}
pub fn previous_work_product(&self, tcx: TyCtxt<'_>) -> WorkProduct {
let work_product_id = self.work_product_id();
tcx.dep_graph
.previous_work_product(&work_product_id)
.unwrap_or_else(|| panic!("Could not find work-product for CGU `{}`", self.name()))
}
pub fn items_in_deterministic_order(
&self,
tcx: TyCtxt<'tcx>,
) -> Vec<(MonoItem<'tcx>, MonoItemData)> {
// The codegen tests rely on items being process in the same order as
// they appear in the file, so for local items, we sort by span first
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct ItemSortKey<'tcx>(Option<Span>, SymbolName<'tcx>);
// We only want to take HirIds of user-defines instances into account.
// The others don't matter for the codegen tests and can even make item
// order unstable.
fn local_item_id<'tcx>(item: MonoItem<'tcx>) -> Option<DefId> {
match item {
MonoItem::Fn(ref instance) => match instance.def {
InstanceKind::Item(def) => def.as_local().map(|_| def),View on GitHub (pinned to 22057b88b0)
Solutions
- Delete the incremental cache and rebuild: `cargo clean && cargo build` (fastest reliable fix).
- If a full clean is too costly, remove only `target/<triple>/incremental/` (and `target/debug/incremental/`).
- Disable incremental compilation for the run: `CARGO_INCREMENTAL=0 cargo build` or pass `-C incremental=no`.
- Stop wiping/reusing `target/` across toolchain switches; pin one toolchain per workspace (rustup override) and clean when switching.
- Stop committing or rsyncing `target/` between machines; treat it as machine-scoped, not portable.
Example fix
# before (mismatched incremental state) $ cargo build # prior run crashed; target/incremental half-written $ cargo build # panic: Could not find work-product for CGU `...` # after $ rm -rf target/debug/incremental target/<triple>/incremental $ cargo build # rebuilds incremental DB cleanly
Defensive patterns
Strategy: fallback
Try / catch
// Missing work-product for a CGU usually means the incremental cache is stale/corrupt.
use std::process::Command;
fn build_without_incremental(dir: &str) -> std::io::Result<std::process::ExitStatus> {
// First try: incremental on (fast path).
let s1 = Command::new("cargo").args(["build"]).current_dir(dir).status()?;
if s1.success() { return Ok(s1); }
// Fallback: wipe incremental artifacts and rebuild with incremental off.
Command::new("cargo").args(["clean"]).current_dir(dir).status()?;
Command::new("cargo")
.args(["build", "--config", "profile.dev.incremental=false"])
.current_dir(dir)
.status()
} Prevention
- Disable incremental compilation in CI: `CARGO_INCREMENTAL=0`.
- Clean `target/` after switching branches that change crate graphs.
- Never share a `target/` directory across different toolchain versions.
- If pinning nightly, record the exact commit hash so CGU layout stays stable.
When it happens
Trigger: Fires during incremental codegen when `tcx.dep_graph.previous_work_product(&work_product_id)` returns `None` for a CGU that the current run believes should have been cached. Commonly triggered by external mutation of `target/` between runs, a crashed prior compilation that left a half-written incremental DB, or a `cargo` invocation that changed the codegen-unit layout (e.g. different `codegen-units`, panic strategy, or `-C prefer-dynamic`) without invalidating the cache.
Common situations: Developers hit this after killing a build mid-flight (`Ctrl-C` during codegen), after a disk-full / crash, after switching toolchains without `cargo clean`, after editing `Cargo.toml`'s `[profile]` codegen settings, or after a CI runner restored a stale incremental cache that doesn't match the current source tree. Also seen when `RUSTC_WRAPPER` (e.g. sccache) returns a cached artifact that disagrees with the local incremental DB.
Related errors
- counting sort fills every slot of a kind's range
- Invalid tag for ClearCrossCrate: {tag:?}
- Incremental cache file size overflowed u64.
- failed to lookup `SourceFile` in new context
- Bad hash {:?} (map {:?})
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/2b15d1814c10da93.json.
Report an issue: GitHub.