risingwavelabs/risingwave · error
Watermark should not be produced by a table function
Error message
Watermark should not be produced by a table function
What it means
In a ProjectSet (project with table functions), watermarks can only be transformed by scalar select items. When the project's watermark expression lands on a `Set` item (a table function), there is no single output row to carry a derived watermark, so the executor bails with this error. Table functions expand one input row to many rows, which makes watermark propagation ill-defined.
Source
Thrown at src/stream/src/executor/project/project_set.rs:305
}
async fn handle_watermark(&self, watermark: Watermark) -> StreamExecutorResult<Vec<Watermark>> {
let expr_indices = match self.watermark_derivations.get_vec(&watermark.col_idx) {
Some(v) => v,
None => return Ok(vec![]),
};
let mut ret = vec![];
for expr_idx in expr_indices {
let expr_idx = *expr_idx;
let derived_watermark = match &self.select_list[expr_idx] {
ProjectSetSelectItem::Scalar(expr) => {
watermark
.clone()
.transform_with_expr(expr, expr_idx + PROJ_ROW_ID_OFFSET)
.await
}
ProjectSetSelectItem::Set(_) => {
bail!("Watermark should not be produced by a table function");
}
};
if let Some(derived_watermark) = derived_watermark {
ret.push(derived_watermark);
} else {
warn!(
"a NULL watermark is derived with the expression {}!",
expr_idx
);
}
}
Ok(ret)
}
}
/// Either a scalar expression or a set-returning function.
///View on GitHub (pinned to 6469eb736d)
Solutions
- Restructure the query so the watermark column comes from a scalar projection, not a table function output (e.g. compute the watermark column before applying the table function)
- Remove the watermark declaration on that column if it is not needed downstream
- Split the query: materialize the table-function output first, then define watermarks on the materialized result
- If the planner emitted this shape automatically, file an issue with the SQL query
Defensive patterns
Strategy: validation
Validate before calling
-- Validate at query-design time: the watermark column must come from a scalar projection -- Ensure no table function (generate_series, unnest, ...) sits between the source watermark column and its consumers SELECT column_name FROM watermarked_columns WHERE column_name NOT IN (SELECT output_col_of_table_function);
Try / catch
match pipeline_build {
Err(e) if e.to_string().contains("Watermark should not be produced by a table function") => {
// Rewrite the query: emit the watermark column via scalar projection before the table function
rewrite_query_without_watermark_through_tvf();
}
other => other?,
} Prevention
- Define watermarks only on plain columns of the source/table, never on table-function outputs
- Compute derived watermark columns in a scalar projection upstream of any table function
- If watermarks through table functions are needed, materialize the function output first and redeclare watermarks there
- Review query plans for ProjectSet items before enabling watermark propagation
When it happens
Trigger: Building/planning a streaming pipeline whose watermark columns pass through a table function (e.g. generate_series, unnest) inside a ProjectSet, so `handle_watermark` meets a `ProjectSetSelectItem::Set` at the watermark's expr index.
Common situations: Defining a source/table with watermark columns and then joining/aliasing through table-function expressions in the streaming query; planner bugs letting a watermark expr reference a table-function output.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Watermark cannot be NULL
- Watermark serde should have at least one order type
- below watermark check condition eval must return bool array
- unreachable (prev != curr)
- GenerateSeriesExecutor should not have child!
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/db9c04c7bfa17d81.
Report an issue: GitHub.