{"id":"a1d6d207a4ad862e","repo":"rust-lang/rust","slug":"bad-hash-map","errorCode":null,"errorMessage":"Bad hash {:?} (map {:?})","messagePattern":"Bad hash (.+?) \\(map (.+?)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_middle/src/query/on_disk_cache.rs","lineNumber":580,"sourceCode":"\n    fn decode_expn_id(&mut self) -> ExpnId {\n        let hash = ExpnHash::decode(self);\n        if hash.is_root() {\n            return ExpnId::root();\n        }\n\n        if let Some(expn_id) = ExpnId::from_hash(hash) {\n            return expn_id;\n        }\n\n        let krate = self.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id());\n\n        let expn_id = if krate == LOCAL_CRATE {\n            // We look up the position of the associated `ExpnData` and decode it.\n            let pos = self\n                .expn_data\n                .get(&hash)\n                .unwrap_or_else(|| panic!(\"Bad hash {:?} (map {:?})\", hash, self.expn_data));\n\n            let data: ExpnData =\n                self.with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA));\n            let expn_id = rustc_span::hygiene::register_local_expn_id(data, hash);\n\n            #[cfg(debug_assertions)]\n            {\n                use rustc_data_structures::stable_hash::{StableHash, StableHasher};\n                let local_hash = self.tcx.with_stable_hashing_context(|mut hcx| {\n                    let mut hasher = StableHasher::new();\n                    expn_id.expn_data().stable_hash(&mut hcx, &mut hasher);\n                    hasher.finish()\n                });\n                debug_assert_eq!(hash.local_hash(), local_hash);\n            }\n\n            expn_id\n        } else {","sourceCodeStart":562,"sourceCodeEnd":598,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_middle/src/query/on_disk_cache.rs#L562-L598","documentation":"`CacheDecoder::decode_expn_id` (on_disk_cache.rs:580) looks up the byte position of an `ExpnData` record by its `ExpnHash` in the `expn_data` map persisted in the cache footer. A miss (`.unwrap_or_else(|| panic!(\"Bad hash {:?} (map {:?})\", hash, self.expn_data))`) means the expansion's hygiene hash stored inside an encoded query result has no corresponding entry in the local crate's `expn_data` table.","triggerScenarios":"Reached when decoding a `Span`/`ExpnId` from the incremental cache and `ExpnId::from_hash(hash)` returns `None` for a `LOCAL_CRATE` hash, then the footer's `expn_data: UnhashMap<ExpnHash, AbsoluteBytePos>` also lacks it. Indicates the cache and the live hygiene context disagree on which macro expansions existed in the local crate.","commonSituations":"Typical after a proc-macro or declarative-macro definition changed between sessions but the incremental cache wasn't invalidated (e.g. macro_rules in a dependency edited without bumping its version, a proc-macro crate rebuilt without `cargo clean`, an editor/IDE that rewrites files mid-build, or a CI layer that restored a stale incremental dir).","solutions":["Delete `target/<triple>/incremental/` (and `target/debug/incremental/`) and rebuild.","If you edit a proc-macro or macro_rules crate, run `cargo clean -p <macro-crate>` before rebuilding dependents.","Run `cargo clean && cargo build` to rule out cascading stale caches.","Disable incremental compilation for the affected workflow: `CARGO_INCREMENTAL=0`.","Ensure your CI cache key includes the full source hash of proc-macro crates and the toolchain version.","Report to rust-lang/rust if it reproduces after a clean build, with `rustc -vV` and the macro setup."],"exampleFix":"# before\n$ vim my-proc-macro/src/lib.rs   # change macro hygiene\n$ cargo build                    # panic: Bad hash ExpnHash{...} (map {...})\n\n# after\n$ cargo clean -p my-proc-macro\n$ cargo build                    # dependents rebuild with fresh hygiene table","handlingStrategy":"validation","validationCode":"// Verify the incremental cache file's recorded hash matches its bytes before reuse,\n// to catch corruption from disk errors, partial writes, or cross-FS copies.\nuse std::fs;\nuse std::path::Path;\nuse std::collections::hash_map::DefaultHasher;\nuse std::hash::{Hash, Hasher};\nfn cache_intact(path: &Path, expected_hash: u64) -> bool {\n    let Ok(bytes) = fs::read(path) else { return false };\n    let mut h = DefaultHasher::new();\n    bytes.hash(&mut h);\n    h.finish() == expected_hash\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not copy `target/` between machines or filesystems — incremental blobs are not portable.","Ensure builds finish before syncing/snapshotting the workspace (partial writes corrupt the hash map).","Validate hardware: silent disk corruption (bit rot) commonly surfaces as hash mismatches.","Treat any `Bad hash` panic as cache corruption: `cargo clean` and rebuild."],"tags":["rustc","incremental-compilation","on-disk-cache","hygiene","macro-expansion","cache-corruption"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}