risingwavelabs/risingwave · error · StreamExecutorError
Gap interval expression returned null
Error message
Gap interval expression returned null
What it means
During gap-fill executor initialization (`execute_inner`), the gap interval expression is evaluated once against an empty dummy row. Because the interval expression must be constant, evaluating to NULL is a fatal configuration error, so the executor returns an anyhow error "Gap interval expression returned null" instead of proceeding. Gap fill needs a concrete positive interval to know how far to advance the time column for each missing window.
Source
Thrown at src/stream/src/executor/eowc/eowc_gap_fill.rs:365
#[try_stream(ok = Message, error = StreamExecutorError)]
async fn execute_inner(self) {
let Self {
input,
inner: mut this,
} = self;
let mut input = input.execute();
let barrier = expect_first_barrier(&mut input).await?;
let first_epoch = barrier.epoch;
yield Message::Barrier(barrier);
this.prev_row_table.init_epoch(first_epoch).await?;
// Calculate and validate gap interval once at initialization
let dummy_row = OwnedRow::new(vec![]);
let interval_datum = this.gap_interval.eval_row_infallible(&dummy_row).await;
let interval = interval_datum
.ok_or_else(|| anyhow::anyhow!("Gap interval expression returned null"))?
.into_interval();
// Validate that gap interval is positive.
if interval <= Interval::from_month_day_usec(0, 0, 0) {
Err(anyhow::anyhow!("Gap interval must be positive"))?;
}
let mut vars = ExecutionVars {
staging_prev_rows: HashMap::new(),
};
#[for_await]
for msg in input {
match msg? {
// Drop the time watermark: a late anchor makes gap fill back-fill below it.
// PARTITION BY column watermarks (if any) pass through unchanged.
Message::Watermark(watermark)
if this.partition_by_indices.contains(&watermark.col_idx) =>View on GitHub (pinned to 6469eb736d)
Solutions
- Replace the gap interval expression with a non-null constant literal such as `INTERVAL '1 minute'` in the gap-fill options.
- If the interval is computed, precompute the value and inline the literal; the executor only evaluates against an empty row, so anything row-dependent yields NULL.
- Check the MV WITH clause/options for a typo that drops the interval value so it defaults to NULL.
- Recreate the materialized view with a corrected gap interval.
Example fix
// before WITH (gap_interval = NULL) // after WITH (gap_interval = INTERVAL '5 minutes')
Defensive patterns
Strategy: validation
Validate before calling
-- Ensure the gap interval is a non-null constant: SELECT INTERVAL '5 minutes' IS NOT NULL AS gap_ok; -- true -- Use only constant literals in gap_fill options, never column references.
Prevention
- Always pass a literal interval (e.g., INTERVAL '1 minute') as the gap interval.
- Never reference columns or session variables in the gap interval expression.
- Review generated SQL templates so the interval value is never dropped or NULL.
When it happens
Trigger: Creating an EMIT ON WINDOW CLOSE query with a gap-fill `gap_interval` (or `GAP`/interval argument) expression that evaluates to NULL at runtime — e.g., the interval literal is NULL, or the expression references a column/value that is NULL on an empty row.
Common situations: Typing `gap_interval => NULL` in the WITH options; using a parameterized or computed expression that is non-constant or NULL rather than a literal like `INTERVAL '5 minutes'`; copy-pasting config where the interval value was lost.
Related errors
- Gap interval must be positive
- to use iceberg engine table, the variable `iceberg_engine_co
- Scalar subquery might produce more than one row.
- Expected at most 1 clean_watermark_index per table, got {:?}
- Session parameters error: {0}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/9bef9670f19fd10d.
Report an issue: GitHub.