risingwavelabs/risingwave · error
unreachable (non-comparator or unexpected bound state)
Error message
unreachable (non-comparator or unexpected bound state)
What it means
In get_range, when exactly one of curr/prev is Some, the code matches on self.comparator (PbExprNodeType) to build the new range bound. Only GreaterThan, GreaterThanOrEqual, LessThan and LessThanOrEqual are valid comparators for a dynamic filter; anything else hits `unreachable!()`. The panic means the executor was constructed with a comparator node type that is not one of the four supported comparison operators.
Source
Thrown at src/stream/src/executor/dynamic_filter.rs:229
}
/// Returns the required range, whether the latest value is in lower bound (rather than upper)
/// and whether to insert or delete the range.
fn get_range(
&self,
curr: &Datum,
prev: Datum,
) -> ((Bound<ScalarImpl>, Bound<ScalarImpl>), bool, bool) {
debug_assert_ne!(curr, &prev);
let curr_is_some = curr.is_some();
match (curr.clone(), prev) {
(Some(c), None) | (None, Some(c)) => {
let range = match self.comparator {
GreaterThan => (Excluded(c), Unbounded),
GreaterThanOrEqual => (Included(c), Unbounded),
LessThan => (Unbounded, Excluded(c)),
LessThanOrEqual => (Unbounded, Included(c)),
_ => unreachable!(),
};
let is_insert = curr_is_some;
// The new bound is always towards the last known value
let is_lower = matches!(self.comparator, GreaterThan | GreaterThanOrEqual);
(range, is_lower, is_insert)
}
(Some(c), Some(p)) => {
if c.default_cmp(&p).is_lt() {
let range = match self.comparator {
GreaterThan | LessThanOrEqual => (Excluded(c), Included(p)),
GreaterThanOrEqual | LessThan => (Included(c), Excluded(p)),
_ => unreachable!(),
};
let is_insert = matches!(self.comparator, GreaterThan | GreaterThanOrEqual);
(range, true, is_insert)
} else {
// c > p
let range = match self.comparator {View on GitHub (pinned to 6469eb736d)
Solutions
- Check the DynamicFilterExecutor::new call site / planner code that chooses the PbExprNodeType and ensure it is restricted to the four ordering comparators.
- If a new comparator (e.g. Equal/NotEqual) is intended, add the corresponding arm to all match statements in get_range instead of relying on `_ => unreachable!()`.
- Validate the comparator in the constructor and return a descriptive error at executor creation rather than panicking later.
Example fix
// before
_ => unreachable!(),
// after
other => return Err(StreamExecutorError::internal(anyhow::anyhow!(
"unsupported dynamic filter comparator: {:?}", other
))), Defensive patterns
Strategy: validation
Validate before calling
// At executor construction:
const SUPPORTED: [PbExprNodeType; 4] = [GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual];
assert!(SUPPORTED.contains(&comparator), "unsupported dynamic filter comparator: {:?}", comparator); Type guard
fn is_ordering_comparator(c: &PbExprNodeType) -> bool {
matches!(c, PbExprNodeType::GreaterThan | PbExprNodeType::GreaterThanOrEqual | PbExprNodeType::LessThan | PbExprNodeType::LessThanOrEqual)
} Try / catch
match self.comparator {
GreaterThan => (Excluded(c), Unbounded),
GreaterThanOrEqual => (Included(c), Unbounded),
LessThan => (Unbounded, Excluded(c)),
LessThanOrEqual => (Unbounded, Included(c)),
other => return Err(StreamExecutorError::internal(anyhow::anyhow!("unsupported comparator {:?}", other))),
} Prevention
- Validate the comparator in DynamicFilterExecutor::new instead of trusting the planner.
- Add a planner-side exhaustive check so only ordering comparators can instantiate a dynamic filter.
- When adding comparators, grep for all `unreachable!()` sites in get_range and update each.
When it happens
Trigger: get_range (invoked from execute_inner while processing a barrier/epoch on the right-side value) is called on a DynamicFilterExecutor whose `comparator` field was set to a PbExprNodeType outside {GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual} (e.g. Equal, or a non-comparison node).
Common situations: Hit by developers adding new comparator support to dynamic filters without updating all match arms in get_range, or by planner changes that instantiate a DynamicFilterExecutor with an unexpected expression node type (e.g. testing with Equal instead of an ordering comparison).
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- unreachable (non-comparator)
- dynamic filter condition eval must return bool array
- below watermark check condition eval must return bool array
- IcebergSinkWriter should be initialized before barrier
- BatchPosixFsReader should not hit this branch. refer to `bat
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/22964d202e158201.
Report an issue: GitHub.