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
- Delete the incremental cache and rebuild: `rm -rf target/<triple>/incremental target/debug/incremental` then `cargo build`.
- Run `cargo clean` (full) if a per-crate clean isn't enough.
- After deleting/renaming public items in a library, run `cargo clean -p <library>` before rebuilding dependents.
- If a CI layer caches `target/`, key it on the source hash of every upstream crate the build links, not just the leaf.
- Disable incremental compilation as a stopgap: `CARGO_INCREMENTAL=0`.
- 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
- Clean after adding/removing crates from the workspace — DefPathHashes are stable only per crate-graph.
- Keep proc-macro crate versions in lockstep across the graph; re-exports shift DefPathHash mapping.
- Pin dependencies (Cargo.lock) so dependent identity hashes do not drift.
- Avoid `cargo` builds that mix `--features` toggles against the same `target/` without a clean.
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
- Incremental cache file size overflowed u64.
- failed to lookup `SourceFile` in new context
- Bad hash {:?} (map {:?})
- counting sort fills every slot of a kind's range
- Invalid tag for ClearCrossCrate: {tag:?}
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/8c73372fec7050ac.json.
Report an issue: GitHub.