rust-lang/rust · critical

Failed to convert DefPathHash {def_path_hash:?}

Error message

Failed to convert DefPathHash {def_path_hash:?}

What it means

`CacheDecoder::decode_def_id` (on_disk_cache.rs:662) reads a `DefPathHash` from the cache and calls `tcx.def_path_hash_to_def_id(hash)` to remap it to the current session's `DefId`. A `None` result triggers `panic!("Failed to convert DefPathHash {def_path_hash:?}")`. The cache's contract is that every `DefPathHash` written into a query result corresponds to a definition that still exists in the current session—so a miss means that definition disappeared (item removed/renamed) without proper cache invalidation.

Source

Thrown at compiler/rustc_middle/src/query/on_disk_cache.rs:662

        let cnum = self.tcx.stable_crate_id_to_crate_num(stable_id);
        cnum
    }

    // Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two
    // compilation sessions. We use the `DefPathHash`, which is stable across
    // sessions, to map the old `DefId` to the new one.
    fn decode_def_id(&mut self) -> DefId {
        // Load the `DefPathHash` which is was we encoded the `DefId` as.
        let def_path_hash = DefPathHash::decode(self);

        // Using the `DefPathHash`, we can lookup the new `DefId`.
        // Subtle: We only encode a `DefId` as part of a query result.
        // If we get to this point, then all of the query inputs were green,
        // which means that the definition with this hash is guaranteed to
        // still exist in the current compilation session.
        match self.tcx.def_path_hash_to_def_id(def_path_hash) {
            Some(r) => r,
            None => panic!("Failed to convert DefPathHash {def_path_hash:?}"),
        }
    }

    fn decode_attr_id(&mut self) -> rustc_span::AttrId {
        panic!("cannot decode `AttrId` with `CacheDecoder`");
    }
}

impl<'a, 'tcx> BlobDecoder for CacheDecoder<'a, 'tcx> {
    fn decode_symbol(&mut self) -> Symbol {
        self.decode_symbol_or_byte_symbol(
            Symbol::new,
            |this| Symbol::intern(this.read_str()),
            |opaque| Symbol::intern(opaque.read_str()),
        )
    }

    fn decode_byte_symbol(&mut self) -> ByteSymbol {

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Delete the incremental cache and rebuild: `rm -rf target/<triple>/incremental target/debug/incremental` then `cargo build`.
  2. Run `cargo clean` (full) if a per-crate clean isn't enough.
  3. After deleting/renaming public items in a library, run `cargo clean -p <library>` before rebuilding dependents.
  4. If a CI layer caches `target/`, key it on the source hash of every upstream crate the build links, not just the leaf.
  5. Disable incremental compilation as a stopgap: `CARGO_INCREMENTAL=0`.
  6. If reproducible after a full clean, report to rust-lang/rust with `rustc -vV`, the `DefPathHash`, and both crate versions.

Example fix

# before
$ rm src/old_module.rs        # remove an item another crate cached a DefId for
$ cargo build                 # panic: Failed to convert DefPathHash {...}

# after
$ cargo clean -p downstream-crate
$ cargo build                 # recomputes query results against the new def table
Defensive patterns

Strategy: fallback

Try / catch

// DefPathHash conversion fails when the encoded crate identity no longer matches
// the current DefPathHash map (e.g. dep graph changed under a reused cache).
use std::process::Command;
fn build_robust(dir: &str) -> std::io::Result<std::process::ExitStatus> {
    let s = Command::new("cargo").args(["build"]).current_dir(dir).status()?;
    if s.success() { return Ok(s); }
    // Drop the dep-graph / incremental artifacts, then fall back to a non-incremental build.
    let _ = Command::new("cargo").args(["clean"]).current_dir(dir).status();
    Command::new("cargo").env("CARGO_INCREMENTAL", "0").arg("build").current_dir(dir).status()
}

Prevention

When it happens

Trigger: Fires when an incremental-cache record references a `DefId` whose `DefPathHash`—stable across sessions by design—no longer maps to any definition in the live crate metadata. The classic cause is removing, renaming, or relocating an item (fn/type/trait/impl) that another crate's cached query result pointed at, while the incremental DB still considers the dependent query green.

Common situations: Very common after refactoring a shared library crate (deleting/renaming public items) while a downstream crate reuses a stale `target/incremental`. Also seen with `sccache`/`cargo-cache` mis-keying, after `git checkout`/`git rebase` across a commit that removed defs, or when a workspace member is removed from `Cargo.toml` but `target/` is retained.

Related errors


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