risingwavelabs/risingwave · error

Time column should be Timestamp or Timestamptz

Error message

Time column should be Timestamp or Timestamptz

What it means

In the EOWC (emit-on-window-close) gap-fill executor, `generate_filled_rows` fills gaps in the output by advancing the time column. The code matches on the time column's scalar ref type and only handles Timestamp and Timestamptz; any other type falls into `_ => unreachable!("Time column should be Timestamp or Timestamptz")`. The executor assumes the schema was already validated so the gap-fill time column is a temporal type.

Source

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

            }
        }

        // Generate filled rows, applying the appropriate strategy for each column
        while fill_time < curr_time {
            let mut new_row_data = Vec::with_capacity(prev_row.len());

            for col_idx in 0..prev_row.len() {
                let datum = if col_idx == time_column_index {
                    // Time column: use the incremented timestamp
                    let fill_time_scalar = match prev_time_scalar {
                        ScalarRefImpl::Timestamp(_) => ScalarImpl::Timestamp(fill_time),
                        ScalarRefImpl::Timestamptz(_) => {
                            let micros = fill_time.0.and_utc().timestamp_micros();
                            ScalarImpl::Timestamptz(
                                risingwave_common::types::Timestamptz::from_micros_uncheck(micros),
                            )
                        }
                        _ => unreachable!("Time column should be Timestamp or Timestamptz"),
                    };
                    Some(fill_time_scalar)
                } else if partition_col_mask[col_idx] {
                    prev_row.datum_at(col_idx).to_owned_datum()
                } else if let Some(strategy) = fill_columns.get(&col_idx) {
                    // Apply the fill strategy for this column
                    match strategy {
                        FillStrategy::Locf => prev_row.datum_at(col_idx).to_owned_datum(),
                        FillStrategy::Null => None,
                        FillStrategy::Interpolate => {
                            // Apply interpolation step and update cumulative value
                            if let Some(step) = &interpolation_steps[col_idx] {
                                apply_interpolation_step(&mut interpolation_states[col_idx], step);
                                interpolation_states[col_idx].clone()
                            } else {
                                // If interpolation step is None, fill with NULL
                                None
                            }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Cast the gap-fill time column to TIMESTAMP or TIMESTAMPTZ in the query, e.g. `time_col::timestamptz`, before applying gap fill.
  2. Check the source schema with `SHOW COLUMNS FROM <source>` and fix the connector/protobuf definition so the event time is declared as a timestamp type.
  3. If the schema changed after MV creation, recreate the materialized view against the corrected source schema.
  4. Report as a bug if the column is genuinely TIMESTAMP — this implies a validation gap between planning and execution.

Example fix

// before: gap fill on an untyped column
CREATE MATERIALIZED VIEW mv WITH (gap_fill...) AS SELECT ts_col FROM src;
// after: ensure a temporal type
CREATE MATERIALIZED VIEW mv WITH (gap_fill...) AS SELECT ts_col::timestamptz AS ts_col FROM src;
Defensive patterns

Strategy: validation

Validate before calling

-- Verify the gap-fill time column type before creating the MV:
SHOW COLUMNS FROM src;
-- Ensure the time column is TIMESTAMP or TIMESTAMPTZ; cast if not:
SELECT ts_col::timestamptz IS NOT NULL AS ok FROM src LIMIT 1;

Prevention

When it happens

Trigger: Running an EMIT ON WINDOW CLOSE gap-fill query whose time column (the column being advanced by the gap interval) is not Timestamp or Timestamptz — e.g., a Date, Int, or Varchar column used as the fill time, while a gap interval is configured.

Common situations: Defining a gap-fill materialized view over a source where the event-time column was inferred as VARCHAR or BIGINT instead of TIMESTAMP; or a schema change upstream (connector emits a different type) that silently changes the column type after the MV was created.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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