risingwavelabs/risingwave · error · StreamExecutorError

Gap interval must be positive

Error message

Gap interval must be positive

What it means

After the gap interval is resolved (see the null check), the EOWC gap-fill executor validates that the interval is strictly greater than zero using `interval <= Interval::from_month_day_usec(0, 0, 0)`. A zero or negative gap interval would cause the fill loop to make no progress or behave nonsensically, so initialization fails fast with "Gap interval must be positive".

Source

Thrown at src/stream/src/executor/eowc/eowc_gap_fill.rs:370

        } = 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) =>
                {
                    yield Message::Watermark(watermark);
                }
                Message::Watermark(_) => continue,
                Message::Chunk(chunk) => {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Set the gap interval to a strictly positive value, e.g. `INTERVAL '1 minute'`, and recreate the materialized view.
  2. If the interval comes from application code or a template, validate it is > 0 before generating the SQL.
  3. If zero gap is intended, remove the gap-fill option entirely instead of passing a zero interval.
  4. Double-check negative interval strings (`-INTERVAL ...`) and unary minus in generated SQL.

Example fix

// before
WITH (gap_interval = INTERVAL '0 seconds')
// after
WITH (gap_interval = INTERVAL '1 minute')
Defensive patterns

Strategy: validation

Validate before calling

-- Validate the interval is strictly positive before use:
SELECT INTERVAL '5 minutes' > INTERVAL '0' AS positive; -- must be true

Prevention

When it happens

Trigger: Executing a gap-fill query whose constant gap interval evaluates to zero or negative — e.g., `INTERVAL '0 seconds'`, `INTERVAL '-5 minutes'`, or a negative computed literal that survives the constant evaluation.

Common situations: Templated/generated SQL where the interval parameter was substituted as 0 or a negative number; experimenting with gap fill and passing 0 to mean 'no gap' instead of omitting the option; a sign typo in an interval string.

Related errors


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