rust-lang/rust-analyzer · error

Failed to make ast node `{node}` from text `{text}`

Error message

Failed to make ast node `{node}` from text `{text}`

What it means

`ast_from_text(_with_edition)` in `syntax::ast::make` parses a snippet as a `SourceFile` and casts the first descendant to the requested AST node type `N`. If the tree contains no node of type `N` (malformed text or a different syntactic category), it panics with the type name and text. Nearly all `make::*` constructors route through this helper, so this panic means a constructor was handed text it cannot build its node from.

Source

Thrown at crates/syntax/src/ast/make.rs:1406

    };
    let node = node.clone_subtree();
    assert_eq!(node.syntax().text_range().start(), 0.into());
    node
}

#[track_caller]
fn ast_from_text<N: AstNode>(text: &str) -> N {
    ast_from_text_with_edition(text, Edition::CURRENT)
}

#[track_caller]
fn ast_from_text_with_edition<N: AstNode>(text: &str, edition: Edition) -> N {
    let parse = SourceFile::parse(text, edition);
    let node = match parse.tree().syntax().descendants().find_map(N::cast) {
        Some(it) => it,
        None => {
            let node = std::any::type_name::<N>();
            panic!("Failed to make ast node `{node}` from text `{text}`")
        }
    };
    let node = node.clone_subtree();
    assert_eq!(node.syntax().text_range().start(), 0.into());
    node
}

pub fn token(kind: SyntaxKind) -> SyntaxToken {
    tokens::SOURCE_FILE
        .tree()
        .syntax()
        .descendants_with_tokens()
        .filter_map(|it| it.into_token())
        .find(|it| it.kind() == kind)
        .unwrap_or_else(|| panic!("unhandled token: {kind:?}"))
}

pub mod tokens {

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Read `{node}` and `{text}` in the panic to identify which constructor template produced unparseable output.
  2. Fix the caller so interpolated fragments are syntactically valid (validate identifiers/paths before formatting).
  3. Reproduce the text and run `SourceFile::parse(text, edition)` to see parse errors, then adjust the template.
  4. If the syntax is edition-dependent, call the `_with_edition` constructor with the correct `Edition`.
  5. Add a unit test for the `make::` constructor with the previously failing input.

Example fix

// before (panics: `1 +` is not a valid path)
let p = make::path_from_text("1 +");
// after
let p = make::path_from_text("std::mem::drop");
Defensive patterns

Strategy: validation

Validate before calling

fn ast_text_is_valid(text: &str, edition: span::Edition) -> bool {
    let parse = syntax::SourceFile::parse(text, edition);
    parse.errors().is_empty()
}

Type guard

fn as_ast_node<N: syntax::AstNode>(text: &str, edition: span::Edition) -> Option<N> {
    let parse = syntax::SourceFile::parse(text, edition);
    parse.tree().syntax().descendants().find_map(N::cast)
}

Prevention

When it happens

Trigger: Any `make::*` constructor whose `format!` template was filled with malformed content — a bad name/signature in `make::fn_`, an invalid path in `make::path_from_text`, unbalanced delimiters, or an empty string — so `SourceFile::parse` yields no descendant castable to `N`.

Common situations: Buggy assists/syntax rewrites interpolating user identifiers containing spaces or operators into templates; empty-string arguments; edition-sensitive syntax fed with the wrong `Edition`; regressions after editing a `make::` template format string.

Related errors


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