{"record":{"id":"bec37b96a5978615","repo":"risingwavelabs/risingwave","slug":"unknown-fill-strategy","errorCode":null,"errorMessage":"unknown fill strategy: {}","messagePattern":"unknown fill strategy: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/stream/src/from_proto/eowc_gap_fill.rs","lineNumber":65,"sourceCode":"        // Parse interval from ExprNode\n        let interval_expr_node = node.get_interval()?;\n        let gap_interval =\n            build_non_strict_from_prost(interval_expr_node, params.eval_error_report.clone())?;\n\n        let fill_columns: Vec<usize> = node\n            .get_fill_columns()\n            .iter()\n            .map(|&x| x as usize)\n            .collect();\n\n        let fill_strategies: Vec<FillStrategy> = node\n            .get_fill_strategies()\n            .iter()\n            .map(|s| match s.as_str() {\n                \"locf\" => Ok(FillStrategy::Locf),\n                \"interpolate\" => Ok(FillStrategy::Interpolate),\n                \"null\" => Ok(FillStrategy::Null),\n                _ => anyhow::bail!(\"unknown fill strategy: {}\", s),\n            })\n            .collect::<anyhow::Result<_>>()?;\n\n        let fill_columns_with_strategies: HashMap<usize, FillStrategy> =\n            fill_columns.into_iter().zip_eq(fill_strategies).collect();\n\n        let vnodes = params.vnode_bitmap.map(Arc::new);\n\n        let prev_row_table =\n            StateTableBuilder::new(node.get_prev_row_table().as_ref().unwrap(), store, vnodes)\n                .forbid_preload_all_rows()\n                .build()\n                .await;\n\n        let partition_by_indices: Vec<usize> = node\n            .get_partition_by_indices()\n            .iter()\n            .map(|&x| x as usize)","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/stream/src/from_proto/eowc_gap_fill.rs#L47-L83","documentation":"The EOWC (emit-on-window-close) gap fill conversion validates each fill strategy string from the proto node against the known set: \"locf\", \"interpolate\", \"null\". Any other string fails conversion in `new_boxed_executor` with this error naming the unknown value.","triggerScenarios":"A `FillNode`/gap-fill proto carries a fill strategy string other than locf/interpolate/null, so the `match s.as_str()` arm hits the `_ => bail!` branch during actor building.","commonSituations":"Frontend (newer version) emitting a fill strategy the stream node (older version) does not know; a typo or corrupt value in the plan proto; hand-crafted test plans with bogus strategy strings.","solutions":["Use one of the supported strategies: locf, interpolate, or null.","Upgrade the stream compute node so its FillStrategy enum matches the frontend's emitted values.","Inspect the fragment proto to find which strategy string is being sent and correct the query/planner."],"exampleFix":"-- before\nGAP FILL (FILL-strategy 'linear')\n-- after\nGAP FILL (... FILL ('locf')) -- or 'interpolate' / 'null'","handlingStrategy":"validation","validationCode":"// before sending the proto\nconst VALID: [&str; 3] = [\"locf\", \"interpolate\", \"null\"];\nassert!(strategies.iter().all(|s| VALID.contains(&s.as_str())), \"unknown fill strategy\");","typeGuard":"fn is_valid_strategy(s: &str) -> bool {\n    matches!(s, \"locf\" | \"interpolate\" | \"null\")\n}","tryCatchPattern":null,"preventionTips":["Only use documented fill strategies in queries","Keep the frontend's strategy list and the stream enum in sync","Upgrade frontend and stream nodes together"],"tags":["streaming","gap-fill","config-validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}