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

  1. Check the DynamicFilterExecutor::new call site / planner code that chooses the PbExprNodeType and ensure it is restricted to the four ordering comparators.
  2. 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!()`.
  3. 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

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


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