rust-lang/rust-analyzer · error

Can't find SyntaxNodePtr {:?} in AstIdMap: {:?}

Error message

Can't find SyntaxNodePtr {:?} in AstIdMap:
{:?}

What it means

`AstIdMap::erased_ast_id` maps a SyntaxNodePtr back to the compact FileAstId previously assigned for that node. If the pointer is not present in the map, the invariant 'every node whose ast_id is requested was seen when the map was built' is broken, so it panics with the pointer and a dump of all mapped nodes. AstIdMaps are rebuilt whenever the file changes, so this usually indicates a stale map paired with nodes from a different version of the file.

Source

Thrown at crates/span/src/ast_id.rs:825

    }

    pub fn ast_id_for_ptr<N: AstIdNode>(&self, ptr: AstPtr<N>) -> FileAstId<N> {
        let ptr = ptr.syntax_node_ptr();
        FileAstId { raw: self.erased_ast_id(ptr), _marker: PhantomData }
    }

    /// Blocks may not be allocated (if they have no items), so they have a different API.
    pub fn ast_id_for_ptr_for_block(
        &self,
        ptr: AstPtr<ast::BlockExpr>,
    ) -> Option<FileAstId<ast::BlockExpr>> {
        let ptr = ptr.syntax_node_ptr();
        self.try_erased_ast_id(ptr).map(|raw| FileAstId { raw, _marker: PhantomData })
    }

    fn erased_ast_id(&self, ptr: SyntaxNodePtr) -> ErasedFileAstId {
        self.try_erased_ast_id(ptr).unwrap_or_else(|| {
            panic!(
                "Can't find SyntaxNodePtr {:?} in AstIdMap:\n{:?}",
                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);

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Ensure the AstIdMap and the syntax nodes come from the same parse/version of the file (same salsa revision)
  2. Rebuild the AstIdMap for the current file instead of reusing a cached one
  3. Verify nodes created by fixup/expansion are either added to the map or never queried for ast ids
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the map and node come from the same parse before mapping:
if map.file_version() != parse.version() {
    map = rebuild_ast_id_map(parse); // re-query the AstIdMap for the current revision
}
let id = map.erased_ast_id(ptr);

Try / catch

// Only during debugging/tests, not in production paths:
let id = std::panic::catch_unwind(AssertUnwindSafe(|| map.erased_ast_id(ptr.clone())))
    .ok()?; // then re-resolve from a fresh map

Prevention

When it happens

Trigger: Calling `erased_ast_id` (directly or via `FileAstId::new`-style APIs or the `check_all_nodes` validation) with a SyntaxNodePtr that was never inserted — e.g. a node created for a newer/older parse than the AstIdMap in use, a synthetic/fixup node never added to the map, or a node from a different file.

Common situations: Salsa query staleness where an AstIdMap input changed but a dependent query kept old node pointers; macro-expansion or syntax-fixup paths producing nodes outside the original file map; test code (`check_all_nodes`) walking nodes of a mismatched parse.

Related errors


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