BoundaryML/baml · error · StrongAstError

An element at was a token when it should have been a node.

Error message

An element at {at:?} was a token when it should have been a node.

What it means

StrongAstError::ShouldBeToken is the inverse of ShouldBeNode: the FromCST conversion expected a leaf token (e.g. a keyword, identifier, or punctuation) at position `at` but found a composite node instead. Conversion stops because the AST builder cannot consume a node where a token is required.

Solutions

  1. Inspect the source at the `at` range and correct the syntax (avoid wrapping the expected token in an unexpected construct)
  2. Resolve all parser errors before formatting to prevent recovery-induced wrapping
  3. Sync parser and baml_fmt versions so token/node shapes agree
  4. If constructing CSTs programmatically, insert tokens (not nodes) where tokens are required
Defensive patterns

Strategy: type-guard

Validate before calling

// verify the element at the position is a token, not a node
let el = parent.children_with_tokens().next()?;
if matches!(el, SyntaxElement::Node(_)) {
    return Err("expected token, found node");
}

Type guard

fn is_token(e: &SyntaxElement) -> bool { matches!(e, SyntaxElement::Token(_)) }

Try / catch

match StrongAst::from_cst(node) {
    Ok(ast) => use(ast),
    Err(StrongAstError::ShouldBeToken { at }) => {
        report(at, "expected a token but found a node");
    }
    Err(e) => report_all(e),
}

Prevention

When it happens

Trigger: A conversion helper that reads the next token (e.g. expects a specific keyword or punctuation) encounters a nested node at that position in the parent — the CST nests an element the grammar says should be flat.

Common situations: Grammar changes nesting previously-flat tokens; parser recovery wrapping tokens in error nodes; mismatched parser/baml_fmt versions; programmatically built trees with wrong child granularity.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/759f4294e34967fd. Report an issue: GitHub.

Appendix: source

Thrown at baml_language/crates/baml_fmt/src/ast/mod.rs:76

    #[error("Expected token/node of kind {expected:?}, but was unable to find it in {parent:?}")]
    MissingExpectedElement {
        expected: SyntaxKind,
        parent: TextRange,
    },
    /// When an element is expected (not of a single specific [`SyntaxKind`]) but there were no more children left.
    #[error("Expected token/node {desc}, but was unable to find it in {parent:?}")]
    MissingExpectedElementDesc {
        desc: Cow<'static, str>,
        parent: TextRange,
    },
    /// When the node isn't expected to have any more children (e.g. a statement found a `;`) but there are still children left.
    #[error("Unexpected additional element at {at:?} in {parent:?}")]
    UnexpectedAdditionalElement { parent: TextRange, at: TextRange },
    /// When an element is expected to be a node but it's actually a token.
    #[error("An element at {at:?} was a node when it should have been a token.")]
    ShouldBeNode { at: TextRange },
    /// When an element is expected to be a token but it's actually a node.
    #[error("An element at {at:?} was a token when it should have been a node.")]
    ShouldBeToken { at: TextRange },
}
impl StrongAstError {
    /// Checks that the given node is of the specified [`SyntaxKind`].
    ///
    /// # Errors
    /// Returns [`StrongAstError::UnexpectedKind`] if the element not the expected kind.
    pub fn assert_kind_node(node: &SyntaxNode, expected: SyntaxKind) -> Result<(), Self> {
        if node.kind() == expected {
            Ok(())
        } else {
            Err(Self::UnexpectedKind {
                expected,
                found: node.kind(),
                at: node.text_range(),
            })
        }
    }

View on GitHub (pinned to bd85ce9dee)