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
- Add an explicit initial value, e.g. pass `initial:0` (or the appropriate identity) to the fold/aggregate so the empty tuple is valid.
- Check why the tuple is empty: inspect the column list/interpolation feeding the aggregate and ensure at least one column is produced.
- 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
- Always supply `initial:` when folding over dynamically generated column lists
- Assert non-empty column lists before emitting aggregate code in generators
- Add a unit test for the empty-columns case in any PRQL-generating tool
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
- expected a table or query, found an empty tuple
- `take`: expected early or late, found
- std.from_text: expected a string literal, found
- `format`: expected csv or json, found
- Unexpected: assign to
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)