BoundaryML/baml · error

Unhandled rule

Error message

Unhandled rule: {:?}

What it means

unhandled_rule_to_doc is the formatter's fallback for pest Rules it has no rendering strategy for. When format option fail_on_unhandled_rule is true it errors 'Unhandled rule: {:?}'; otherwise it returns the raw source text unchanged. It is raised from schema_to_doc and the type/field-type doc walkers.

Solutions

  1. Set fail_on_unhandled_rule to false so unknown rules pass through verbatim.
  2. Upgrade baml to a version whose formatter handles the rule named in the message.
  3. Rewrite the offending syntax to a construct the current formatter supports.

Example fix

// before
let options = FormatOptions { indent_width: 2, fail_on_unhandled_rule: true };
// after
let options = FormatOptions { indent_width: 2, fail_on_unhandled_rule: false };
Defensive patterns

Strategy: fallback

Try / catch

let options_strict = FormatOptions { indent_width: 2, fail_on_unhandled_rule: true };
let formatted = match format_schema(source, &options_strict) {
    Ok(f) => f,
    Err(_) => format_schema(source, &FormatOptions { indent_width: 2, fail_on_unhandled_rule: false })?,
};

Prevention

When it happens

Trigger: Calling format_schema with fail_on_unhandled_rule: true on a schema containing any construct the formatter version doesn't explicitly handle (newer syntax like certain attributes, generators, or type expressions).

Common situations: Formatting .baml files written for a newer BAML language version with an older formatter, or enabling the strict flag while the file uses rarely-used rules.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at engine/baml-lib/ast/src/formatter/mod.rs:404

                    docs.push(RcDoc::space());
                    docs.push(RcDoc::text("|"));
                    docs.push(RcDoc::space());
                }
                Rule::base_type_with_attr | Rule::non_union => {
                    docs.push(pair_to_doc_text(pair));
                }
                _ => {
                    docs.push(self.unhandled_rule_to_doc(pair)?);
                }
            }
        }

        Ok(())
    }

    fn unhandled_rule_to_doc<'a>(&self, pair: Pair<'a, Rule>) -> Result<RcDoc<'a, ()>> {
        if self.fail_on_unhandled_rule {
            Err(anyhow!("Unhandled rule: {:?}", pair.as_rule()))
        } else {
            // Don't trim the str repr of unhandled rules, so
            // we can see the original source.
            Ok(RcDoc::text(pair.as_str()))
        }
    }
}

fn pair_to_doc_text(pair: Pair<'_, Rule>) -> RcDoc<'_, ()> {
    RcDoc::text(pair.as_str().trim())
}

View on GitHub (pinned to bd85ce9dee)