rust-lang/rust-analyzer · error
AstIdMap node mismatch with node `{ptr:?}`
Error message
AstIdMap node mismatch with node `{ptr:?}` What it means
`AstIdMap::get` converts a stored FileAstId back into a typed AstPtr by first resolving the raw id to a SyntaxNodePtr and then re-typing it via `AstPtr::try_from_raw`. If the resolved pointer cannot be represented as the requested node type `N`, the encoded id/kind do not agree with the stored node — an internal encoding invariant violation — so it panics. This indicates the packed hash/index/kind encoding pointed at an entry whose kind does not match the requested type.
Source
Thrown at crates/span/src/ast_id.rs:845
ptr,
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
)
})
}
fn try_erased_ast_id(&self, ptr: SyntaxNodePtr) -> Option<ErasedFileAstId> {
let hash = hash_ptr(&ptr);
let idx = *self.ptr_map.find(hash, |&idx| self.arena[idx].0 == ptr)?;
Some(self.arena[idx].1)
}
// Don't bound on `AstIdNode` here, because `BlockExpr`s are also valid here (`ast::BlockExpr`
// doesn't always have a matching `FileAstId`, but a `FileAstId<ast::BlockExpr>` always has
// a matching node).
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
let ptr = self.get_erased(id.raw);
AstPtr::try_from_raw(ptr)
.unwrap_or_else(|| panic!("AstIdMap node mismatch with node `{ptr:?}`"))
}
pub fn get_erased(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
let hash = hash_ast_id(&id);
match self.id_map.find(hash, |&idx| self.arena[idx].1 == id) {
Some(&idx) => self.arena[idx].0,
None => panic!(
"Can't find ast id {:?} in AstIdMap:\n{:?}",
id,
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
),
}
}
}
#[cfg(not(no_salsa_async_drops))]
impl Drop for AstIdMap {
fn drop(&mut self) {View on GitHub (pinned to e8f7e90aa3)
Solutions
- Verify the FileAstId was produced by the same AstIdMap instance you are querying
- Check pack/unpack of hash, index, and kind bits after changing the encoding
- Add/refresh the `check_all_nodes` consistency test to catch mismatches early
Defensive patterns
Strategy: validation
Validate before calling
// Before get::<N>, confirm the kind matches the requested type:
fn kind_matches<N: AstNode>(map: &AstIdMap, id: FileAstId<N>) -> bool {
map.raw_kind(id.raw) == N::kind()
} Try / catch
let ptr = std::panic::catch_unwind(AssertUnwindSafe(|| map.get::<ast::Item>(id))).ok()?;
Prevention
- Never decode or hand-construct ErasedFileAstId raw values
- After changing the bit packing, run the AstIdMap consistency tests (check_all_nodes)
- Keep ids scoped to the AstIdMap instance that produced them
When it happens
Trigger: Calling `map.get::<N>(id)` with a FileAstId<N> whose stored kind bits don't match N's kind — typically caused by a bug in pack_hash_index_and_kind/unpacking, an id from a different AstIdMap being looked up in the wrong map, or hash collisions corrupting the id_map lookup.
Common situations: Developers changing the ErasedFileAstId bit layout or hashing; cross-file/cross-version id reuse in salsa-cached queries; fuzzing inputs that expose kind-mismatch corruption.
Related errors
- you have way too many items in the same file!
- Can't find SyntaxNodePtr {:?} in AstIdMap: {:?}
- Can't find ast id {:?} in AstIdMap: {:?}
- `ast::DocComment` must have a comment token
- segments are always nested in paths
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/1a17e575975c09a3.
Report an issue: GitHub.