{"record":{"id":"0b5c343abac6a288","repo":"risingwavelabs/risingwave","slug":"iceberg-intermediate-scan-must-have-a-source-catal","errorCode":null,"errorMessage":"iceberg intermediate scan must have a source catalog","messagePattern":"iceberg intermediate scan must have a source catalog","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_iceberg_intermediate_scan.rs","lineNumber":172,"sourceCode":"            time_travel_info,\n            table_column_type_mapping,\n            hummock_rewrite,\n        }\n    }\n\n    pub fn source_catalog(&self) -> Option<&SourceCatalog> {\n        self.core.catalog.as_deref()\n    }\n\n    /// Fields carrying the iceberg-side column types (before the engine-table Hummock\n    /// type remapping), for predicate pushdown. Derived from the source catalog, which\n    /// the remapping never touches.\n    fn iceberg_side_fields(&self) -> Vec<Field> {\n        let catalog = self\n            .core\n            .catalog\n            .as_ref()\n            .expect(\"iceberg intermediate scan must have a source catalog\");\n        let by_name: HashMap<&str, &ColumnCatalog> =\n            catalog.columns.iter().map(|c| (c.name(), c)).collect();\n        self.core\n            .column_catalog\n            .iter()\n            .map(|col| {\n                let source_col = by_name\n                    .get(col.name())\n                    .expect(\"output column must exist in the source catalog\");\n                Field::from(&source_col.column_desc)\n            })\n            .collect()\n    }\n\n    pub fn output_columns(&self) -> impl ExactSizeIterator<Item = &str> {\n        self.core.column_catalog.iter().map(|c| c.name.as_str())\n    }\n","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_iceberg_intermediate_scan.rs#L154-L190","documentation":"`iceberg_side_fields` requires the LogicalIcebergIntermediateScan node to carry a source catalog (`self.core.catalog`); it uses `.expect` to unwrap it. An intermediate Iceberg scan node built without its underlying catalog is an internal invariant violation, so this panics during predicate pushdown when computing Iceberg-side schema fields.","triggerScenarios":"`predicate_pushdown` on a LogicalIcebergIntermediateScan whose `core.catalog` is None, then mapping columns to Iceberg fields.","commonSituations":"Optimizer bugs where an intermediate Iceberg scan node is constructed (e.g. by plan rewriting or tests) without attaching the source catalog before pushdown passes run.","solutions":["Ensure the LogicalIcebergIntermediateScan is always constructed with a Some(catalog) (check its constructor/`with_core` call sites).","Make `iceberg_side_fields` return a Result and bail with a descriptive error instead of panicking.","Reduce to a minimal Iceberg query plan and file a bug with the plan dump and recent changes."],"exampleFix":"// before\nlet catalog = self.core.catalog.as_ref()\n    .expect(\"iceberg intermediate scan must have a source catalog\");\n\n// after\nlet catalog = self.core.catalog.as_ref().ok_or_else(|| {\n    anyhow!(\"iceberg intermediate scan is missing its source catalog\")\n})?;","handlingStrategy":"validation","validationCode":"// before pushdown\nif scan.core.catalog.is_none() {\n    return Err(\"iceberg intermediate scan missing source catalog\".into());\n}","typeGuard":"fn has_source_catalog(scan: &LogicalIcebergIntermediateScan) -> bool {\n    scan.core.catalog.is_some()\n}","tryCatchPattern":"match plan.predicate_pushdown(...) {\n    Err(e) | PanicRescue(e) if e.to_string().contains(\"source catalog\") => {\n        log::error!(\"plan corruption: intermediate scan without catalog\");\n        Err(anyhow!(\"invalid iceberg scan node\"))\n    }\n    r => r,\n}","preventionTips":["Always construct intermediate scans with the source catalog attached","Add constructor tests asserting catalog is Some","Convert expects in optimizer code to Result-based errors","Run Iceberg e2e tests after touching scan node construction"],"tags":["rust","iceberg","optimizer","assertion"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}