BoundaryML/baml · error

Filter function ' ' must return a boolean, got

Error message

Filter function '{}' must return a boolean, got {:?}

What it means

A @watch filter function must evaluate to a boolean so the interpreter can decide whether the watched value changed meaningfully. After executing the filter body, evaluate_filter_function checks the result; any non-Bool result (string, int, null, etc.) triggers this error.

Solutions

  1. Make the filter's final expression an explicit boolean comparison (==, !=, >, <, etc.)
  2. Coerce/normalize early-return paths to return true or false
  3. Log or inspect the returned value ({:?} in the message shows the actual type) to find which path yields a non-bool

Example fix

// before
function changed(v: string) -> bool { v.length() }
// after
function changed(v: string) -> bool { v.length() > 0 }
Defensive patterns

Strategy: type-guard

Validate before calling

fn validate_filter_returns_bool(f: &FunctionDef) -> Result<(), String> {
    match f.return_type { Type::Bool => Ok(()), other => Err(format!("filter must return bool, got {:?}", other)) }
}

Type guard

fn is_bool_result(v: &BamlValue) -> bool { matches!(v, BamlValue::Bool(_)) }

Prevention

When it happens

Trigger: A filter function whose last evaluated expression yields a non-boolean — e.g. returning a comparison result wrapped in a value, returning an int used as truthy, or forgetting a final comparison expression.

Common situations: Filters written in languages where numbers/strings are truthy (e.g. returning value.length() directly); returning null on an early path; forgetting the final boolean expression in an implicit-return block.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at engine/baml-compiler/src/thir/interpret.rs:394

    let noop_watch_handler = crate::watch::shared_noop_handler();

    // Evaluate the function body
    let result = evaluate_block(
        &filter_func.body,
        scopes,
        thir,
        run_llm_function,
        &noop_watch_handler,
        function_name,
    )
    .await?;

    scopes.pop();

    // Extract boolean result
    match result {
        BamlValueWithMeta::Bool(b, _) => Ok(b),
        _ => bail!(
            "Filter function '{}' must return a boolean, got {:?}",
            fn_name,
            result
        ),
    }
}

/// Convert BamlValue to BamlValueWithMeta (with fake metadata)
fn baml_value_to_value_with_meta(value: BamlValue) -> BamlValueWithMeta<ExprMetadata> {
    let meta = (Span::fake(), None);
    match value {
        BamlValue::String(s) => BamlValueWithMeta::String(s, meta),
        BamlValue::Int(i) => BamlValueWithMeta::Int(i, meta),
        BamlValue::Float(f) => BamlValueWithMeta::Float(f, meta),
        BamlValue::Bool(b) => BamlValueWithMeta::Bool(b, meta),
        BamlValue::Map(m) => {
            let converted = m
                .into_iter()

View on GitHub (pinned to bd85ce9dee)