risingwavelabs/risingwave · error · StreamExecutorError
Gap interval expression returned null
Error message
Gap interval expression returned null
What it means
The gap-fill executor evaluates the GAP interval expression once at initialization against an empty dummy row (the interval must be constant). If the expression evaluates to NULL, `execute_inner` returns this anyhow error because there is no valid gap width to drive the filling logic.
Solutions
- Replace the GAP expression with a non-null constant INTERVAL literal, e.g. `GAP INTERVAL '1 minute'`.
- If the gap is computed by an expression, wrap it with COALESCE to a default: `GAP COALESCE(expr, INTERVAL '1 minute')`.
- Verify the referenced column/parameter is not NULL at the time the MV is created.
Example fix
// before SELECT GAP FILL(ts, gap_col) FROM t GROUP BY ts; // after SELECT GAP FILL(ts, INTERVAL '1 minute') FROM t GROUP BY ts;
Defensive patterns
Strategy: validation
Validate before calling
-- Validate the gap expression is non-null before creating the MV: SELECT COALESCE(gap_expr, INTERVAL '1 minute') IS NOT NULL AS ok; -- Prefer a literal: GAP INTERVAL '1 minute'
Try / catch
match gap_fill_mv_creation {
Err(e) if e.to_string().contains("Gap interval expression returned null") => {
// re-create with a constant non-null INTERVAL
}
other => other?,
} Prevention
- Use a constant INTERVAL literal for GAP; do not reference nullable columns.
- Wrap computed gap expressions in COALESCE(expr, default_interval).
- Remember the gap is evaluated once at MV creation, so it must be non-null then.
When it happens
Trigger: A `GAP FILL` query where the GAP expression (e.g. `GAP NULL::INTERVAL`, a column reference that is NULL, or an expression evaluating to NULL under constant folding) yields a NULL datum at src/stream/src/executor/gap_fill.rs:561.
Common situations: Users parameterizing the gap via a nullable column or a subquery instead of a literal; mis-typed expressions that constant-fold to NULL.
Related errors
- Gap interval must be positive
- Gap interval expression returned null
- Time column should be Timestamp or Timestamptz
- {0}
- BatchGapFill is not implemented yet
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/55519049a83fa7c9.
Report an issue: GitHub.
Appendix: source
Thrown at src/stream/src/executor/gap_fill.rs:561
gap_interval,
high_gap_fill_amplification_threshold,
ctx,
input,
metrics,
} = *self;
let mut input = input.execute();
let barrier = expect_first_barrier(&mut input).await?;
let first_epoch = barrier.epoch;
yield Message::Barrier(barrier);
managed_state.init_epoch(first_epoch).await?;
// Calculate and validate gap interval once at initialization
let dummy_row = OwnedRow::new(vec![]);
let interval_datum = gap_interval.eval_row_infallible(&dummy_row).await;
let interval = interval_datum
.ok_or_else(|| anyhow::anyhow!("Gap interval expression returned null"))?
.into_interval();
if interval <= Interval::from_month_day_usec(0, 0, 0) {
Err(anyhow::anyhow!("Gap interval must be positive"))?;
}
let generation_context = GapFillGenerationContext {
metrics: &metrics,
high_amplification_threshold: high_gap_fill_amplification_threshold,
actor_ctx: &ctx,
};
let partition_by_indices = managed_state.partition_by_indices.clone();
let pointer_key_indices = managed_state.pointer_key_indices.clone();
// Interpolation re-slopes every fill, so a changed anchor changes all of them; only
// LOCF/NULL fills can be reused by the diff.
let has_interpolate = fill_columns
.values()
.any(|strategy| matches!(strategy, FillStrategy::Interpolate));View on GitHub (pinned to 6469eb736d)