BoundaryML/baml · error

Failed to convert to string

Error message

Failed to convert to string

What it means

After a successful doc.render into a Vec<u8>, format_schema converts the bytes to a String via String::from_utf8; non-UTF8 output maps to 'Failed to convert to string'. Since BAML source is UTF-8, this indicates corrupted renderer output.

Solutions

  1. Verify the .baml file is valid UTF-8 (iconv/file -bi) before formatting.
  2. Re-encode the input file to UTF-8.
  3. If input is valid UTF-8, this is a formatter bug — report it.

Example fix

// before
let formatted = format_schema(source, options)?;
// after
if std::str::from_utf8(source.as_bytes()).is_err() {
    anyhow::bail!("source is not valid UTF-8");
}
let formatted = format_schema(source, options)?;
Defensive patterns

Strategy: validation

Validate before calling

if std::str::from_utf8(source.as_bytes()).is_err() {
    anyhow::bail!("BAML source must be valid UTF-8 before formatting");
}

Type guard

fn is_valid_utf8(bytes: &[u8]) -> bool {
    std::str::from_utf8(bytes).is_ok()
}

Prevention

When it happens

Trigger: Calling format_schema where the rendered byte buffer contains invalid UTF-8 — practically only from a formatter bug or non-UTF8 input leaking through parsing.

Common situations: Feeding files with invalid UTF-8 bytes (e.g. Latin-1 encoded bytes) that survived pest parsing into the formatter.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

        return Ok(source.to_string());
    }

    let mut schema = BAMLParser::parse(Rule::schema, source)?;
    let schema_pair = schema.next().ok_or(anyhow!("Expected a schema"))?;
    if schema_pair.as_rule() != Rule::schema {
        return Err(anyhow!("Expected a schema"));
    }

    let formatter = Formatter {
        indent_width: format_options.indent_width,
        fail_on_unhandled_rule: format_options.fail_on_unhandled_rule,
    };

    let doc = formatter.schema_to_doc(schema_pair.into_inner())?;
    let mut w = Vec::new();
    doc.render(10, &mut w)
        .map_err(|_| anyhow!("Failed to render doc"))?;
    String::from_utf8(w).map_err(|_| anyhow!("Failed to convert to string"))
}

macro_rules! next_pair {
    ($pairs:ident, $rule:expr) => {{
        loop {
            match $pairs.peek() {
                Some(pair) => {
                    if pair.as_rule() == Rule::NEWLINE {
                        $pairs.next();
                        continue;
                    }
                    if pair.as_rule() != $rule {
                        break Err(anyhow!(
                            "Expected a {:?}, got a {:?} ({}:{})",
                            $rule,
                            pair.as_rule(),
                            file!(),
                            line!()

View on GitHub (pinned to bd85ce9dee)