cube-js/cube · error

Unsupported extension node

Error message

Unsupported extension node: {}

What it means

During LogicalPlan-to-Cube-plan conversion, LogicalPlan::Extension nodes (DataFusion's escape hatch for custom, user-defined plan nodes) have no mapping into Cube's plan language and trigger an explicit panic. The message includes the extension node's schema for diagnostics.

Solutions

  1. Identify the extension node name from the schema in the message and rewrite the query without it
  2. Upgrade CubeSQL — many extension nodes are later given real conversions
  3. Open/consult a Cube issue for the specific operator to add a LogicalPlanLanguage mapping
Defensive patterns

Strategy: try-catch

Try / catch

// catch_unwind around planner entry, since this is a panic not a Result
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| planner.plan(sql)));
match result {
    Ok(plan) => plan,
    Err(p) => { log::error!("plan conversion failed: {:?}", p); Err(planning_failed_response()) }
}

Prevention

When it happens

Trigger: The plan being converted contains a custom/extension operator — e.g. a UDF node, a custom table function, or a DataFusion plugin node injected upstream of the converter.

Common situations: Using DataFusion extension points or third-party extensions with CubeSQL; a CubeSQL version where a newly added builtin operator is still represented as an Extension node; raw SQL that maps to an unsupported function.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/f8cd890cfba2c142. Report an issue: GitHub.

Appendix: source

Thrown at rust/cubesql/cubesql/src/compile/rewrite/converter.rs:821

                self.graph
                    .add(LogicalPlanLanguage::Limit([skip, fetch, input]))
            }
            LogicalPlan::Values(values) => {
                let values = add_data_node!(self, values.values, ValuesValues);
                self.graph.add(LogicalPlanLanguage::Values([values]))
            }
            LogicalPlan::CreateExternalTable { .. } => {
                panic!("CreateExternalTable is not supported");
            }
            LogicalPlan::Explain { .. } => {
                panic!("Explain is not supported");
            }
            LogicalPlan::Analyze { .. } => {
                panic!("Analyze is not supported");
            }
            // TODO
            LogicalPlan::Extension(ext) => {
                panic!("Unsupported extension node: {}", ext.node.schema());
            }
            LogicalPlan::Distinct(distinct) => {
                let input = self.add_logical_plan_replace_params(
                    distinct.input.as_ref(),
                    query_params,
                    ctx,
                )?;
                self.graph.add(LogicalPlanLanguage::Distinct([input]))
            }
            // TODO: Support all
            _ => unimplemented!("Unsupported node type: {:?}", plan),
        })
    }
    fn find_source_table_name(&self, plan: &LogicalPlan) -> Result<Option<String>, CubeError> {
        Ok(match plan {
            LogicalPlan::Projection(node) => self.find_source_table_name(node.input.as_ref())?,
            LogicalPlan::Filter(node) => self.find_source_table_name(node.input.as_ref())?,
            LogicalPlan::Window(node) => self.find_source_table_name(node.input.as_ref())?,

View on GitHub (pinned to 7d981676b3)