influxdata/influxdb · error · DataFusionError
expected at least 1 child execution plan node
Error message
expected at least 1 child execution plan node
What it means
union_multiple_children returns an internal_datafusion_err when given zero child execution plans. It collapses a single child to that child and otherwise builds a UnionExec, neither of which is meaningful for an empty child list.
Solutions
- Ensure at least one child plan exists before calling union_multiple_children
- Handle the empty-children case upstream by returning an EmptyExec/placeholder
- Fix the optimizer rule that produced a union node with no inputs
Example fix
// before let plan = union_multiple_children(children)?; // children may be empty // after ensure!(!children.is_empty(), "union requires at least one child plan"); let plan = union_multiple_children(children)?;
Defensive patterns
Strategy: validation
Validate before calling
if children.is_empty() { return Err(plan_err("union needs at least one child")); } Type guard
fn is_unionable(children: &[Arc<dyn ExecutionPlan>]) -> bool { !children.is_empty() } Try / catch
match union_multiple_children(children) {
Err(e) if e.to_string().contains("expected at least 1 child") => EmptyExec::new(schema).into(),
other => other?,
} Prevention
- Guard optimizer rules that can empty union inputs
- Fall back to EmptyExec for zero children
- Assert child count in plan-builder helpers
When it happens
Trigger: Calling union_multiple_children with an empty Vec of Arc<dyn ExecutionPlan>, e.g. after optimizer rules pruned all inputs of what was a union.
Common situations: Custom optimizer rules removing all union children without removing the union node; plan builders that assemble children lists dynamically and can end up empty; chunk-to-physical-node conversion with no chunks.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- The Record batch is empty
- tried to serialize a parquet file with no rows
- is not a valid data type, values are int64, uint64…
- a supported service limit value is required
- All `DataPoints` must have at least one field. Builder…
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/2554ac11edeb4f22.
Report an issue: GitHub.
Appendix: source
Thrown at core/iox_query/src/util.rs:201
let idx = schema.index_of(name)?;
let mut intervals = calculate_field_intervals(session_state, Arc::clone(&schema), expr)?;
Ok(intervals.swap_remove(idx))
}
/// Take in 1 or more children, and returns a singular [`ExecutionPlan`].
///
/// If there are multiple children, it unions in order to return a singular execution plan.
pub fn union_multiple_children(
mut children: Vec<Arc<dyn ExecutionPlan>>,
) -> Result<Arc<dyn ExecutionPlan>, DataFusionError> {
if children.is_empty() {
return Err(internal_datafusion_err!(
"expected at least 1 child execution plan node"
));
}
if children.len() == 1 {
return Ok(children.pop().expect("unreachable"));
}
UnionExec::try_new(children)
}
#[cfg(test)]
mod tests {
use datafusion::common::ScalarValue;
use datafusion::common::rounding::next_down;
use datafusion::execution::context::SessionContext;
use datafusion::logical_expr::{col, lit};
use schema::{InfluxFieldType, TIME_DATA_TIMEZONE, builder::SchemaBuilder};
use super::*;
fn time_interval(lower: Option<i64>, upper: Option<i64>) -> Interval {
let lower = ScalarValue::TimestampNanosecond(lower, TIME_DATA_TIMEZONE());
let upper = ScalarValue::TimestampNanosecond(upper, TIME_DATA_TIMEZONE());
Interval::try_new(lower, upper).unwrap()View on GitHub (pinned to 06200ef96b)