risingwavelabs/risingwave · critical
only inner join without non-equal condition is supported for
Error message
only inner join without non-equal condition is supported for delta joins
What it means
This is a panic (not a returned error) in the stream fragmenter's DeltaIndexJoin handling. Delta joins only support inner joins with pure equality conditions (no non-equal predicates); any other delta join shape hits an unreachable else branch and panics. It signals a planner/frontend invariant violation rather than a user-recoverable error.
Source
Thrown at src/frontend/src/stream_fragmenter/mod.rs:593
NodeBody::LocalityProvider(_) => {
current_fragment
.fragment_type_mask
.add(FragmentTypeFlag::LocalityProvider);
}
_ => {}
};
// handle join logic
if let NodeBody::DeltaIndexJoin(delta_index_join) = stream_node.node_body.as_mut().unwrap()
{
if delta_index_join.get_join_type()? == JoinType::Inner
&& delta_index_join.condition.is_none()
{
return build_delta_join_without_arrange(state, current_fragment, stream_node);
} else {
panic!("only inner join without non-equal condition is supported for delta joins");
}
}
// Usually we do not expect exchange node to be visited here, which should be handled by the
// following logic of "visit children" instead. If it does happen (for example, `Share` will be
// transformed to an `Exchange`), it means we have an empty fragment and we need to add a no-op
// node to it, so that the meta service can handle it correctly.
if let NodeBody::Exchange(_) = stream_node.node_body.as_ref().unwrap() {
stream_node = state.gen_no_op_stream_node(stream_node);
}
// Visit plan children.
stream_node.input = stream_node
.input
.into_iter()
.map(|mut child_node| {
match child_node.get_node_body()? {
// When exchange node is generated when doing rewrites, it could be havingView on GitHub (pinned to 6469eb736d)
Solutions
- Rewrite the query/MV join to a pure inner equi-join, or move non-equal predicates to WHERE on a single side.
- Drop the index that triggers the delta join path so the join uses a regular hash join.
- File a bug with the query plan (EXPLAIN) — this panic indicates a missing planner validation.
- Use a plain materialized view (without index) for the join and filter on read.
Example fix
-- before CREATE INDEX ON mv_delta (a_id); SELECT * FROM mv_delta JOIN other ON mv_delta.a_id = other.id AND mv_delta.ts > other.ts; -- after SELECT * FROM mv_delta JOIN other ON mv_delta.a_id = other.id WHERE mv_delta.ts > other.ts; -- non-equi predicate outside join condition
Defensive patterns
Strategy: validation
Validate before calling
-- ensure joins feeding indexed MVs are pure equi inner joins -- EXPLAIN the plan and check for DeltaIndexJoin nodes with -- non-inner join types or non-equi ON conditions before CREATE INDEX.
Try / catch
// This is a panic, not an error — guard at plan construction:
let join_type = delta_index_join.get_join_type()?;
let has_non_equi = delta_index_join.condition
.as_ref().map(|c| c.has_non_equi_condition()).unwrap_or(false);
if join_type != JoinType::Inner || has_non_equi {
return build_regular_join(state, current_fragment, stream_node);
} Prevention
- Only create indexes on MVs whose joins are inner equi-joins.
- Move non-equal predicates (range, inequality) out of ON clauses.
- EXPLAIN plans before CREATE INDEX to spot problematic join shapes.
- Report any occurrence upstream — panics here are planner bugs.
When it happens
Trigger: Building a streaming plan whose fragment contains a DeltaIndexJoin node with a join type other than Inner, or an Inner join that also carries a non-equi condition, when build_fragment() fragments the plan.
Common situations: Creating indexes/MVs over joins with mixed ON conditions (e.g. ON a.id = b.id AND a.ts > b.ts) that the delta-join rewrite incorrectly routes through the delta join path; using join types (LEFT/SEMI) on indexed MVs that hit this path.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- unexpected statement type: {:?}
- join plan cannot be generated, tables not connected.
- Scalar subquery might produce more than one row.
- core predicate must exist
- dynamic filter condition eval must return bool array
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/c1a43845764bd0e8.
Report an issue: GitHub.