BoundaryML/baml · error · anyhow::Error

Predicate did not evaluate to a boolean

Error message

Predicate did not evaluate to a boolean

What it means

BAML constraint predicates (@assert/@check) are Jinja expressions rendered with 'this' bound to the value. The rendered output must be the string "true" or "false"; anything else raises this error. It ensures predicates are boolean expressions rather than arbitrary values.

Solutions

  1. Rewrite the predicate to be an explicit boolean comparison, e.g. {{ this.email != "" }}.
  2. Avoid predicates that return raw values; wrap them with a comparison or boolean cast.
  3. Test the expression renders exactly 'true' or 'false' for sample inputs.

Example fix

// before
@assert({{ this.items }})
// after
@assert({{ this.items|length > 0 }})
Defensive patterns

Strategy: validation

Validate before calling

fn is_boolean_predicate(expr: &str) -> bool {
    // naive guard: reject bare 'this.x' returns without a comparison
    let comparisons = [">", "<", ">=", "<=", "==", "!=", "&&", "||", "not ", "|length >"];
    comparisons.iter().any(|c| expr.contains(c))
}

Try / catch

match evaluate_predicate(&value, &expr) {
    Ok(b) => b,
    Err(e) if e.to_string().contains("did not evaluate to a boolean") => {
        eprintln!("predicate '{}' must be a comparison, got a value", expr);
        false
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling evaluate_predicate (directly or via first_failing_assert_nested / run_user_checks) with a predicate expression whose rendered result is neither "true" nor "false" — e.g. {{ this.name }}, {{ this.items|length }}, or an expression producing an object.

Common situations: Writing an @assert that returns the field instead of a comparison; forgetting a comparison operator; using a filter that returns a number/string; predicates like {{ this.email }} intended as 'must be truthy'.

Related errors


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

Appendix: source

Thrown at engine/baml-lib/baml-core/src/ir/jinja_helpers.rs:92

    // In rust string literals, `{` is escaped as `{{`.
    // So producing the string `{{}}` requires writing the literal `"{{{{}}}}"`
    let template = format!(r#"{{{{ {} }}}}"#, expression.0);
    let args_dict = minijinja::Value::from_serialize(ctx);
    Ok(env.render_str(&template, &args_dict)?)
}

// TODO: (Greg) better error handling.
// TODO: (Greg) Upstream, typecheck the expression.
pub fn evaluate_predicate(
    this: &BamlValue,
    predicate_expression: &JinjaExpression,
) -> Result<bool, anyhow::Error> {
    let ctx: HashMap<String, minijinja::Value> =
        HashMap::from([("this".to_string(), minijinja::Value::from_serialize(this))]);
    match render_expression(predicate_expression, &ctx)?.as_ref() {
        "true" => Ok(true),
        "false" => Ok(false),
        _ => Err(anyhow::anyhow!("Predicate did not evaluate to a boolean")),
    }
}

#[cfg(test)]
mod tests {
    use baml_types::BamlValue;

    use super::*;

    #[test]
    fn test_render_expressions() {
        let ctx = vec![
            (
                "a".to_string(),
                BamlValue::List(vec![
                    BamlValue::Int(1),
                    BamlValue::Int(2),
                    BamlValue::Int(3),

View on GitHub (pinned to bd85ce9dee)