PRQL/prql · error · Error

tuple: expected to have at least one entry when initial…

Error message

tuple: expected to have at least one entry when initial value is not provided, found empty tuple

What it means

PRQL's internal `tuple_reduce` fold lowers to a fold over a tuple (list) of expressions. When no initial value was supplied (signaled internally by the sentinel literal `"__missing"`), the tuple must contain at least one element so the first element can seed the fold. An empty tuple with no initial value leaves nothing to accumulate, so the resolver rejects it at compile time.

Solutions

  1. Add an explicit initial value, e.g. pass `initial:0` (or the appropriate identity) to the fold/aggregate so the empty tuple is valid.
  2. Check why the tuple is empty: inspect the column list/interpolation feeding the aggregate and ensure at least one column is produced.
  3. Guard in generating code: if the column list is empty, skip the aggregate or emit a constant instead.

Example fix

// before
aggregate { }
// after
aggregate { total = sum salary }
Defensive patterns

Strategy: validation

Validate before calling

// before compiling/generating PRQL
if (columns.length === 0 && initial === undefined) {
  throw new Error("aggregate over empty tuple requires an `initial:` value");
}

Type guard

const hasInitial = (args) => args.some(a => a.name === 'initial' || a.startsWith('initial:'));

Try / catch

// prqlc reports this at compile time; catch the compiler output
try {
  compile(prqlSource);
} catch (e) {
  if (e.message.includes('at least one entry when initial value is not provided')) {
    // regenerate query with an explicit initial value
  }
}

Prevention

When it happens

Trigger: Writing a PRQL expression that lowers to `tuple_reduce` (e.g. a fold/sum-like aggregate over columns) where the tuple argument expands to zero columns at compile time and no `initial:` parameter is given — e.g. selecting a dynamic column list that resolves to empty, or calling an aggregate with no arguments.

Common situations: Aggregations like `aggregate { }` with an empty tuple, or generated PRQL (from ORMs/tools) that emit an aggregate over a column list that ended up empty because a filter removed all columns or a variable interpolated to nothing.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of PRQL/prql@e164e249b9 (2026-09-09). Data as JSON: /api/errors/7500c535525acf97. Report an issue: GitHub.

Appendix: source

Thrown at prqlc/prqlc/src/semantic/resolver/transforms.rs:357

                    found: write_pl(pattern.clone()),
                })
                .with_span(pattern.span));
            }

            "tuple_reduce" => {
                // yes, this is not a transform, but this is the most appropriate place for it

                let [init, func, list] = unpack::<3>(func.args);
                let list_items = list.kind.into_tuple().unwrap();
                let num_items = list_items.len();
                let mut list_iter = list_items.into_iter();

                let mut res = init.clone();

                if let ExprKind::Literal(Literal::String(init_val)) = &init.kind {
                    if init_val == "__missing" {
                        match num_items {
                            0 => return Err(Error::new(Reason::Expected {
                                who: Some("tuple".to_string()),
                                expected:
                                    "to have at least one entry when initial value is not provided"
                                        .to_string(),
                                found: "empty tuple".to_string(),
                            })
                            .with_span(list.span)
                            .push_hint("try adding an initial:<value> parameter")),
                            1 => {
                                let item = list_iter.next().unwrap();
                                return Ok(item);
                            }
                            _ => {
                                res = list_iter.next().unwrap();
                            }
                        }
                    }
                }

View on GitHub (pinned to e164e249b9)