risingwavelabs/risingwave · error

unreachable (non-comparator)

Error message

unreachable (non-comparator)

What it means

In get_range's (Some(c), Some(p)) branch where the current value c is less than the previous value p, the code maps the comparator to a decreasing range. Only the four ordering comparators are handled; any other comparator value triggers `unreachable!()`. Like the sibling panics, this indicates the executor was built with an unsupported comparison node type.

Source

Thrown at src/stream/src/executor/dynamic_filter.rs:241

            (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 {
                        GreaterThan | LessThanOrEqual => (Excluded(p), Included(c)),
                        GreaterThanOrEqual | LessThan => (Included(p), Excluded(c)),
                        _ => unreachable!(),
                    };
                    let is_insert = matches!(self.comparator, LessThan | LessThanOrEqual);
                    (range, false, is_insert)
                }
            }
            (None, None) => unreachable!(), // prev != curr
        }
    }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Confirm the executor construction only ever uses the four ordering comparators (add a debug_assert in new()).
  2. If a new comparator is intended, extend this match arm and the other two in get_range consistently.
  3. Log the comparator value before the panic (or convert to an internal error) to make the misconfiguration diagnosable.

Example fix

// before
_ => unreachable!(),
// after
other => return Err(StreamExecutorError::internal(anyhow::anyhow!(
    "get_range: unsupported comparator {:?} for decreasing bound", other
))),
Defensive patterns

Strategy: validation

Validate before calling

debug_assert!(matches!(self.comparator, GreaterThan | GreaterThanOrEqual | LessThan | LessThanOrEqual), "bad comparator: {:?}", self.comparator);

Type guard

fn is_ordering_comparator(c: &PbExprNodeType) -> bool {
    matches!(c, PbExprNodeType::GreaterThan | PbExprNodeType::GreaterThanOrEqual | PbExprNodeType::LessThan | PbExprNodeType::LessThanOrEqual)
}

Try / catch

let range = match self.comparator {
    GreaterThan | LessThanOrEqual => (Excluded(c), Included(p)),
    GreaterThanOrEqual | LessThan => (Included(c), Excluded(p)),
    other => return Err(StreamExecutorError::internal(anyhow::anyhow!("unsupported comparator {:?}", other))),
};

Prevention

When it happens

Trigger: execute_inner calls get_range when both the previous and current right-side values are non-null and c.default_cmp(&p).is_lt() (the watermark moved backwards / decreased), while self.comparator is not GreaterThan/GreaterThanOrEqual/LessThan/LessThanOrEqual.

Common situations: Encountered during development of new comparator kinds or when a planner bug passes a wrong PbExprNodeType; the decreasing-value branch is hit when the compared column (often an event-time/ingestion timestamp) can regress.

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/59eb208370cd1420. Report an issue: GitHub.