{"record":{"id":"d9af65be80336486","repo":"risingwavelabs/risingwave","slug":"missing-iceberg-scan","errorCode":null,"errorMessage":"Missing Iceberg Scan","messagePattern":"Missing Iceberg Scan","errorType":"error_code","errorClass":"GenDataFusionPlanError","httpStatus":null,"severity":"error","filePath":"src/frontend/src/datafusion/execute/mod.rs","lineNumber":62,"sourceCode":"use crate::session::SessionImpl;\nuse crate::utils::DropGuard;\n\nmod memory_ctx;\nmod query_planner;\n\npub(crate) use memory_ctx::create_df_spillable_budget_ctx;\nconst DF_MANAGED_SPILL_DIR: &str = \"df_batch_spill/\";\n\n#[derive(Clone)]\npub struct DfBatchQueryPlanResult {\n    pub(crate) plan: Arc<LogicalPlan>,\n    pub(crate) schema: RwSchema,\n    pub(crate) stmt_type: StatementType,\n}\n\n#[derive(Debug, Error)]\npub enum GenDataFusionPlanError {\n    #[error(\"Missing Iceberg Scan\")]\n    MissingIcebergScan,\n    #[error(\"Unsupported Plan Node\")]\n    UnsupportedPlanNode,\n    #[error(\"Generating plan error: {0}\")]\n    Generating(#[source] RwError),\n}\n\npub fn try_gen_datafusion_plan(\n    optimized_logical: &BatchOptimizedLogicalPlanRoot,\n) -> Result<Arc<LogicalPlan>, GenDataFusionPlanError> {\n    use crate::optimizer::DataFusionExecuteCheckerExt;\n\n    let check_result = optimized_logical.plan.check_for_datafusion();\n    if !check_result.have_iceberg_scan {\n        return Err(GenDataFusionPlanError::MissingIcebergScan);\n    }\n    if !check_result.supported {\n        return Err(GenDataFusionPlanError::UnsupportedPlanNode);","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/datafusion/execute/mod.rs#L44-L80","documentation":"Part of `GenDataFusionPlanError`, thrown when converting an optimized RisingWave batch plan into an Apache DataFusion `LogicalPlan` for Iceberg queries. The converter expects the plan to contain an Iceberg scan node; if none is found where one is required, it returns `MissingIcebergScan`.","triggerScenarios":"Calling `try_gen_datafusion_plan` on a batch optimized plan whose leaf/scan node is not an Iceberg scan (e.g. the table resolved to a regular MV or non-Iceberg source), so plan translation finds no IcebergScan to translate.","commonSituations":"Pointing the DataFusion-based Iceberg query path at a table that is not backed by an Iceberg scan; plan rewrites (e.g. pushed-down projections or joins) that reordered or removed the scan node expected by the translator.","solutions":["Ensure the query targets an actual Iceberg table/source so the optimized plan contains an Iceberg scan.","Check the plan translation code: verify it inspects the correct plan node position for the Iceberg scan.","Route non-Iceberg tables to the normal batch execution engine instead of the DataFusion path."],"exampleFix":"// before\nlet df_plan = try_gen_datafusion_plan(&optimized_plan)?; // plan from a non-iceberg table\n// after\nif !plan_is_iceberg_scan(&optimized_plan) {\n    return Err(anyhow!(\"target must be an Iceberg table\"));\n}\nlet df_plan = try_gen_datafusion_plan(&optimized_plan)?;","handlingStrategy":"try-catch","validationCode":"// confirm the target is an Iceberg-backed source before using the DataFusion path\nconst src = await client.query(\"SELECT connector FROM rw_sources WHERE name = $1\", [t]);","typeGuard":"function isIcebergSource(row) { return row && String(row.connector).toLowerCase() === \"iceberg\"; }","tryCatchPattern":"match try_gen_datafusion_plan(&plan) {\n    Err(GenDataFusionPlanError::MissingIcebergScan) => {\n        // fall back to the normal batch engine\n    }\n    r => r?,\n}","preventionTips":["Only route Iceberg-backed table queries through the DataFusion execution path","Inspect the optimized plan before translation to confirm an Iceberg scan node exists","Keep plan-translator tests covering the iceberg scan leaf"],"tags":["datafusion","iceberg","query-planning","batch"],"backgroundTag":"missing-dependency","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}