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

  1. Verify the FileAstId was produced by the same AstIdMap instance you are querying
  2. Check pack/unpack of hash, index, and kind bits after changing the encoding
  3. 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

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


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/1a17e575975c09a3. Report an issue: GitHub.