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

  1. Delete the incremental cache and rebuild: `cargo clean && cargo build` (fastest reliable fix).
  2. If a full clean is too costly, remove only `target/<triple>/incremental/` (and `target/debug/incremental/`).
  3. Disable incremental compilation for the run: `CARGO_INCREMENTAL=0 cargo build` or pass `-C incremental=no`.
  4. Stop wiping/reusing `target/` across toolchain switches; pin one toolchain per workspace (rustup override) and clean when switching.
  5. 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

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


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/2b15d1814c10da93.json. Report an issue: GitHub.