{"record":{"id":"a536d32460f367f5","repo":"BoundaryML/baml","slug":"filter-function-must-take-exactly-1-parameter-current-value","errorCode":null,"errorMessage":"Filter function '{}' must take exactly 1 parameter (current value)","messagePattern":"Filter function '(.+?)' must take exactly 1 parameter \\(current value\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-compiler/src/thir/interpret.rs","lineNumber":351,"sourceCode":"    scopes: &mut Vec<Scope>,\n    thir: &THir<ExprMetadata>,\n    run_llm_function: &mut F,\n    function_name: &str,\n) -> Result<bool>\nwhere\n    F: LlmHandler<Fut>,\n    Fut: LlmFuture,\n{\n    // Look up the filter function\n    let filter_func = thir\n        .expr_functions\n        .iter()\n        .find(|f| f.name == fn_name.to_string())\n        .with_context(|| format!(\"Filter function '{fn_name}' not found\"))?;\n\n    // Check arity\n    if filter_func.parameters.len() != 1 {\n        bail!(\n            \"Filter function '{}' must take exactly 1 parameter (current value)\",\n            fn_name\n        );\n    }\n\n    // Convert BamlValue to BamlValueWithMeta\n    log::debug!(\"Filter function current_value: {current_value:?}\");\n    let value_with_meta = baml_value_to_value_with_meta(current_value.clone());\n\n    // Create a new scope with the function parameter\n    // Mark this as a filter context to prevent infinite recursion\n    scopes.push(Scope {\n        variables: [(\n            filter_func.parameters[0].name.clone(),\n            Arc::new(Mutex::new(value_with_meta)),\n        )]\n        .into_iter()\n        .collect(),","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-compiler/src/thir/interpret.rs#L333-L369","documentation":"Filter functions used with @watch must accept exactly one parameter — the current value of the watched variable. The interpreter validates arity before invoking the filter and aborts with this error when the declared function has any other number of parameters.","triggerScenarios":"Declaring a filter function with 0 or 2+ parameters and referencing it from a @watch filter attribute; check_watch_changes then calls evaluate_filter_function, which enforces parameters.len() == 1.","commonSituations":"Writing a filter that also takes config/extra arguments; copy-pasting a multi-argument helper into a watch filter; forgetting to wrap extra context in a closure-like single-arg function.","solutions":["Change the filter function to take exactly one parameter (the current value)","Move any additional data the filter needs into the function body or constants instead of extra parameters","Rename to a different dedicated single-argument filter function and point the @watch attribute at it"],"exampleFix":"// before\nfunction isLong(value: string, min: int) -> bool { ... }\n// after\nfunction isLong(value: string) -> bool { value.length() > 10 }","handlingStrategy":"validation","validationCode":"fn validate_filter_arity(f: &FunctionDef) -> Result<(), String> {\n    if f.parameters.len() == 1 { Ok(()) }\n    else { Err(format!(\"filter '{}' must take exactly 1 parameter\", f.name)) }\n}","typeGuard":"fn is_valid_watch_filter(f: &FunctionDef) -> bool { f.parameters.len() == 1 }","tryCatchPattern":null,"preventionTips":["Always declare watch filters with exactly one parameter (the current value)","Move auxiliary data into constants or the function body, not extra parameters","Review @watch filter attributes against their target function signatures during code review"],"tags":["interpreter","watch","filter","arity","validation"],"backgroundTag":"missing-required-argument","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"}