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

  1. Ensure at least one child plan exists before calling union_multiple_children
  2. Handle the empty-children case upstream by returning an EmptyExec/placeholder
  3. 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

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


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)