{"record":{"id":"f37d74bd784bbee8","repo":"influxdata/influxdb","slug":"no-statistics-for-input-plan","errorCode":null,"errorMessage":"No statistics for input plan","messagePattern":"No statistics for input plan","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/iox_query/src/statistics/aggregate_per_plan.rs","lineNumber":190,"sourceCode":"                    .is_some()\n            {\n                true\n            } else if plan.as_any().downcast_ref::<ProjectionExec>().is_some() {\n                // ProjectionExec is a special case. Only union stats if it includes pure columns\n                projection_includes_pure_columns(\n                    plan.as_any().downcast_ref::<ProjectionExec>().unwrap(),\n                )\n            } else {\n                false\n            };\n\n            // pop statistics of all inputs from the stack\n            let num_inputs = plan.children().len();\n            // num_input must > 0. Pop the first one\n            let mut statistics = self\n                .statistics\n                .pop_back()\n                .expect(\"No statistics for input plan\");\n            // pop the rest and update the min and max\n            for _ in 1..num_inputs {\n                let input_statistics = self\n                    .statistics\n                    .pop_back()\n                    .expect(\"No statistics for input plan\");\n\n                if union_stats {\n                    // Conservatively union min max\n                    statistics.null_count = statistics.null_count.add(&input_statistics.null_count);\n                    statistics.max_value = statistics.max_value.max(&input_statistics.max_value);\n                    statistics.min_value = statistics.min_value.min(&input_statistics.min_value);\n                    statistics.distinct_count = Precision::Absent;\n                };\n            }\n\n            if union_stats {\n                self.statistics.push_back(statistics);","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/core/iox_query/src/statistics/aggregate_per_plan.rs#L172-L208","documentation":"The per-plan statistics aggregation visitor keeps a stack of input statistics that must mirror the plan tree traversal: every child plan pushed statistics during pre_visit. When post_visit pops the stack and it is empty, the traversal invariant is broken; expect() panics with 'No statistics for input plan'.","triggerScenarios":"Running the statistics aggregation over a logical plan where a custom/modified plan node or an overridden pre_visit skipped pushing statistics for a child, so post_visit finds the stack empty.","commonSituations":"Integrating custom LogicalPlan nodes not recognized by the statistics source; patched DataFusion plan visitors; bugs in statistics collection after planner upgrades changing node child counts.","solutions":["Ensure every custom plan node is handled in pre_visit so its input statistics get pushed.","Check the statistics source covers all node types in your plan (no unimplemented branches).","Update the iox_query statistics aggregation after DataFusion version upgrades that alter plan structures.","In a fork, replace expect with returning an internal-error Result for cleaner failure."],"exampleFix":"// before\nlet statistics = self.statistics.pop_back().expect(\"No statistics for input plan\");\n// after\nlet statistics = self.statistics.pop_back().ok_or_else(|| {\n    internal_err!(\"No statistics for input plan\")\n})?;","handlingStrategy":"try-catch","validationCode":"// before running aggregation, ensure every plan node type is handled:\n// walk the plan and confirm each node's children are known to the stats source\nassert_plan_nodes_supported(&plan);","typeGuard":null,"tryCatchPattern":"// this is a panic from expect(); catch via catch_unwind at integration boundaries only\nlet result = std::panic::catch_unwind(|| compute_statistics(&plan));\nmatch result {\n    Ok(stats) => stats,\n    Err(_) => /* fall back to default statistics */,\n}","preventionTips":["Handle all custom plan nodes in the statistics pre_visit so inputs are always pushed.","Keep statistics aggregation in sync after DataFusion upgrades.","Prefer returning Result over expect for stack underflow in a fork.","Add a debug assertion that stack depth matches plan children counts during traversal."],"tags":["query","statistics","panic","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}