risingwavelabs/risingwave · error · StreamExecutorError
Watermark serde should have at least one order type
Error message
Watermark serde should have at least one order type
What it means
After confirming the watermark value is non-NULL, the state table reads the watermark serde's order types to determine ascending/descending direction. This error fires when `watermark_serde.get_order_types()` returns an empty vector, meaning the serde was constructed without any ordering information, so the comparison direction cannot be computed.
Source
Thrown at src/stream/src/common/table/state_table.rs:2206
.await
.map(|s| s.boxed());
};
let watermark_row = watermark_serde.deserialize(&watermark_bytes)?;
if watermark_row.len() != 1 {
return Err(StreamExecutorError::from(format!(
"Watermark row should have exactly 1 column, got {}",
watermark_row.len()
)));
}
let watermark_value = watermark_row[0].clone();
// StateTableInner::update_watermark should ensure that the watermark is not NULL
if watermark_value.is_none() {
return Err(StreamExecutorError::from(anyhow!(
"Watermark cannot be NULL"
)));
}
let order_type = watermark_serde.get_order_types().get(0).ok_or_else(|| {
StreamExecutorError::from(anyhow!(
"Watermark serde should have at least one order type"
))
})?;
let direction = if order_type.is_ascending() {
WatermarkDirection::Ascending
} else {
WatermarkDirection::Descending
};
let clean_watermark_index_in_pk = self
.pk_indices
.iter()
.position(|&i| i == clean_watermark_index);
let clean_watermark_index_in_value = match &self.value_indices {
Some(value_indices) => value_indices
.iter()
.position(|idx| *idx == clean_watermark_index)
.ok_or_else(|| {View on GitHub (pinned to 6469eb736d)
Solutions
- Inspect where the WatermarkSerde is constructed and ensure it is built with the watermark column's order type
- Verify the stream plan fragment includes the sort/order type for the watermark column
- Rebuild/upgrade to a version where serde construction matches the state table API
- If persistent, log the full order_types vector at serde build time to confirm it is empty at creation
Example fix
// before let serde = WatermarkSerde::new(vec![], data_types); // after let serde = WatermarkSerde::new(vec![OrderType::Ascending], data_types);
Defensive patterns
Strategy: validation
Validate before calling
fn check_serde(serde: &WatermarkSerde) -> Result<(), StreamExecutorError> {
if serde.get_order_types().is_empty() {
Err(StreamExecutorError::from(anyhow!("watermark serde has no order types")))
} else { Ok(()) }
} Type guard
fn serde_has_order_type(serde: &WatermarkSerde) -> bool { !serde.get_order_types().is_empty() } Prevention
- Always construct WatermarkSerde with at least one OrderType
- Assert serde construction in executor init with debug_assert!
- Cover serde construction in unit tests for every watermark operator
When it happens
Trigger: Calling the state table watermark update path with a WatermarkSerde built from an empty order-types slice — typically a serde misconstructed in executor/table initialization.
Common situations: Incorrect column list passed when building the serde during fragment/graph dispatch; a refactor or version change that dropped the order type from serde construction; malformed stream plan with no sort column on the watermark key.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Watermark cannot be NULL
- below watermark check condition eval must return bool array
- unreachable (prev != curr)
- Watermark should not be produced by a table function
- You can't use the macro on this type
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/2ecb7cc5001f00ab.
Report an issue: GitHub.