{"record":{"id":"b827eeb78ba291ea","repo":"BoundaryML/baml","slug":"filter-function-must-return-a-boolean-got","errorCode":null,"errorMessage":"Filter function '{}' must return a boolean, got {:?}","messagePattern":"Filter function '(.+?)' must return a boolean, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-compiler/src/thir/interpret.rs","lineNumber":394,"sourceCode":"    let noop_watch_handler = crate::watch::shared_noop_handler();\n\n    // Evaluate the function body\n    let result = evaluate_block(\n        &filter_func.body,\n        scopes,\n        thir,\n        run_llm_function,\n        &noop_watch_handler,\n        function_name,\n    )\n    .await?;\n\n    scopes.pop();\n\n    // Extract boolean result\n    match result {\n        BamlValueWithMeta::Bool(b, _) => Ok(b),\n        _ => bail!(\n            \"Filter function '{}' must return a boolean, got {:?}\",\n            fn_name,\n            result\n        ),\n    }\n}\n\n/// Convert BamlValue to BamlValueWithMeta (with fake metadata)\nfn baml_value_to_value_with_meta(value: BamlValue) -> BamlValueWithMeta<ExprMetadata> {\n    let meta = (Span::fake(), None);\n    match value {\n        BamlValue::String(s) => BamlValueWithMeta::String(s, meta),\n        BamlValue::Int(i) => BamlValueWithMeta::Int(i, meta),\n        BamlValue::Float(f) => BamlValueWithMeta::Float(f, meta),\n        BamlValue::Bool(b) => BamlValueWithMeta::Bool(b, meta),\n        BamlValue::Map(m) => {\n            let converted = m\n                .into_iter()","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-compiler/src/thir/interpret.rs#L376-L412","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make the filter's final expression an explicit boolean comparison (==, !=, >, <, etc.)","Coerce/normalize early-return paths to return true or false","Log or inspect the returned value ({:?} in the message shows the actual type) to find which path yields a non-bool"],"exampleFix":"// before\nfunction changed(v: string) -> bool { v.length() }\n// after\nfunction changed(v: string) -> bool { v.length() > 0 }","handlingStrategy":"type-guard","validationCode":"fn validate_filter_returns_bool(f: &FunctionDef) -> Result<(), String> {\n    match f.return_type { Type::Bool => Ok(()), other => Err(format!(\"filter must return bool, got {:?}\", other)) }\n}","typeGuard":"fn is_bool_result(v: &BamlValue) -> bool { matches!(v, BamlValue::Bool(_)) }","tryCatchPattern":null,"preventionTips":["End every filter function with an explicit boolean comparison","Make all early-return paths return true or false","Avoid relying on truthiness of ints/strings; BAML does not coerce","Annotate filter functions with a bool return type so mismatches surface earlier"],"tags":["interpreter","watch","filter","type-mismatch","boolean"],"backgroundTag":"type-mismatch","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}