cube-js/cube · error
Unexpected join node: {:?}
Error message
Unexpected join node: {:?} What it means
to_logical_plan builds joins from select IR nodes; each join input must be a recognized join-language node. When a join operand node has an unexpected shape, the converter panics with 'Unexpected join node' and the debug dump of the node.
Source
Thrown at rust/cubesql/cubesql/src/compile/rewrite/converter.rs:2193
.collect::<Result<Vec<_>, CubeError>>()?;
let group_expr =
match_expr_list_node!(node_by_id, to_expr, params[3], WrappedSelectGroupExpr);
let aggr_expr =
match_expr_list_node!(node_by_id, to_expr, params[4], WrappedSelectAggrExpr);
let window_expr =
match_expr_list_node!(node_by_id, to_expr, params[5], WrappedSelectWindowExpr);
let from = Arc::new(self.to_logical_plan(params[6])?);
let joins = match_list_node!(node_by_id, params[7], WrappedSelectJoins)
.into_iter()
.map(|j| {
if let LogicalPlanLanguage::WrappedSelectJoin(params) = j {
let input = Arc::new(self.to_logical_plan(params[0])?);
let join_expr = to_expr(params[1])?;
let join_type =
match_data_node!(node_by_id, params[2], WrappedSelectJoinJoinType);
Ok((input, join_expr, join_type))
} else {
panic!("Unexpected join node: {:?}", j)
}
})
.collect::<Result<Vec<_>, _>>()?;
let filter_expr =
match_expr_list_node!(node_by_id, to_expr, params[8], WrappedSelectFilterExpr);
let having_expr =
match_expr_list_node!(node_by_id, to_expr, params[9], WrappedSelectHavingExpr);
let limit = match_data_node!(node_by_id, params[10], WrappedSelectLimit);
let offset = match_data_node!(node_by_id, params[11], WrappedSelectOffset);
let order_expr =
match_expr_list_node!(node_by_id, to_expr, params[12], WrappedSelectOrderExpr);
let alias = match_data_node!(node_by_id, params[13], WrappedSelectAlias);
let distinct = match_data_node!(node_by_id, params[14], WrappedSelectDistinct);
let push_to_cube =
match_data_node!(node_by_id, params[15], WrappedSelectPushToCube);
let filter_expr = normalize_cols(View on GitHub (pinned to 7d981676b3)
Solutions
- Rewrite the join using standard INNER/LEFT joins with simple ON conditions
- Replace subquery-in-join patterns with CTEs and join those
- Upgrade CubeSQL; if reproducible with plain SQL, file an issue with the dumped node
Example fix
// before SELECT ... FROM a, LATERAL (SELECT ...) b // after WITH b AS (SELECT ...) SELECT ... FROM a JOIN b ON ...
Defensive patterns
Strategy: try-catch
Validate before calling
// Reject join shapes known to fail conversion before sending
fn validate_joins(stmt: &SqlStatement) -> Result<(), String> {
for j in stmt.joins() {
if matches!(j.kind, JoinKind::Lateral | JoinKind::Asymmetric | JoinKind::CrossApply) {
return Err(format!("join kind {:?} may not be supported", j.kind));
}
if j.on_condition().contains_subquery() { return Err("subqueries in ON clauses are unsupported".into()); }
}
Ok(())
} Try / catch
let res = std::panic::catch_unwind(AssertUnwindSafe(|| planner.plan(sql)));
if res.is_err() {
// surface 'Unexpected join node' as a 4xx with guidance to simplify the join
return bad_request("Query uses a join shape the planner cannot convert; rewrite with standard INNER/LEFT joins");
} Prevention
- Prefer INNER/LEFT joins with simple ON conditions
- Express lateral/correlated joins as CTEs joined on explicit keys
- Avoid subqueries inside join conditions
When it happens
Trigger: A SELECT with a JOIN whose operand (input, join expression, or join type) doesn't match the expected LogicalPlanLanguage variants — e.g. exotic join types (lateral, asymmetric), complex join conditions, or joins on unsupported constructs.
Common situations: SQL API queries using JOIN types/conditions CubeSQL's planner can't model (FULL OUTER with complex ON, LATERAL joins, subquery joins in unusual positions); older drivers emitting legacy join syntax.
Related errors
- Unsupported filter operator: {}
- Expected filter but found {:?}
- Unexpected logical plan node: {:?}
- This query doesnt have a plan, because it already has values
- Should be rewritten with UtcTimestamp function
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/178086aa745c0f90.
Report an issue: GitHub.