risingwavelabs/risingwave · error

unknown fill strategy

Error message

unknown fill strategy: {}

What it means

Same family as the EOWC variant: the gap fill conversion maps each strategy string from the proto node to a `FillStrategy`, accepting only "locf", "interpolate", and "null"; anything else aborts `new_boxed_executor` with this message naming the string.

Solutions

  1. Use a supported fill strategy: locf, interpolate, or null.
  2. Align frontend and stream node versions so both support the same strategies.
  3. Inspect the plan proto for the offending strategy string and fix the source query/planner.

Example fix

-- before
FILL ('carry-forward')
-- after
FILL ('locf') -- or 'interpolate' / 'null'
Defensive patterns

Strategy: validation

Validate before calling

// before sending the proto
const VALID: [&str; 3] = ["locf", "interpolate", "null"];
assert!(strategies.iter().all(|s| VALID.contains(&s.as_str())), "unknown fill strategy");

Type guard

fn is_valid_strategy(s: &str) -> bool {
    matches!(s, "locf" | "interpolate" | "null")
}

Prevention

When it happens

Trigger: A gap-fill proto node contains a fill strategy string outside the accepted set, so the `match s.as_str()` default arm bails while building the actor.

Common situations: Version skew between frontend emitting newer strategies and an older stream node; malformed or hand-edited plan protos; typos in strategy names in generated plans.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/d0e2a20478a6ada4. Report an issue: GitHub.

Appendix: source

Thrown at src/stream/src/from_proto/gap_fill.rs:63

        // Parse interval from ExprNode
        let interval_expr_node = node.get_interval()?;
        let interval_expr =
            build_non_strict_from_prost(interval_expr_node, params.eval_error_report)?;

        let fill_columns: Vec<usize> = node
            .get_fill_columns()
            .iter()
            .map(|&x| x as usize)
            .collect();

        let fill_strategies: Vec<FillStrategy> = node
            .get_fill_strategies()
            .iter()
            .map(|s| match s.as_str() {
                "locf" => Ok(FillStrategy::Locf),
                "interpolate" => Ok(FillStrategy::Interpolate),
                "null" => Ok(FillStrategy::Null),
                _ => anyhow::bail!("unknown fill strategy: {}", s),
            })
            .collect::<anyhow::Result<_>>()?;

        let fill_columns_with_strategies: HashMap<usize, FillStrategy> =
            fill_columns.into_iter().zip_eq(fill_strategies).collect();

        let partition_by_indices: Vec<usize> = node
            .get_partition_by_indices()
            .iter()
            .map(|&x| x as usize)
            .collect();

        // Hash-distributed state table needs vnodes; singleton (no partition) does not.
        let vnodes = if partition_by_indices.is_empty() {
            None
        } else {
            Some(std::sync::Arc::new(
                params

View on GitHub (pinned to 6469eb736d)