PRQL/prql · error · Error
Unexpected: assign to
Error message
Unexpected: assign to `{alias}` (move assign into the tuple: `[{alias} = ...]`) What it means
This flags a common anti-pattern: assigning an alias to an expression that is already a tuple, like `tuple = (...)` where the expression itself is a tuple. The resolver suggests moving the assignment inside the tuple — `[{alias} = ...]` — because an alias on a tuple is meaningless or a mistake in the select/derive context.
Solutions
- Move the assignment inside the tuple: instead of `alias = {a, b}` write `{alias_a = a, alias_b = b}` or nest as intended.
- Drop the alias if the tuple's fields already have names.
- If you wanted one column from a tuple, extract a field instead of aliasing the whole tuple.
Example fix
// before
select name = {first, last}
// after
select {first, last} Defensive patterns
Strategy: validation
Validate before calling
// when generating select/derive args, reject aliasing a whole tuple
if (arg.alias && arg.expr.kind === 'tuple') throw new Error('move assign inside the tuple: [' + arg.alias + ' = ...]'); Type guard
const aliasesTuple = (arg) => Boolean(arg.alias) && Array.isArray(arg.expr);
Try / catch
try { compile(prql) } catch (e) { if (e.message.includes('Unexpected: assign to')) { /* restructure the select/derive argument */ } } Prevention
- Put aliases on individual fields inside the tuple, not on the tuple itself
- When expanding a column into multiple fields, remove the old alias
- Remember PRQL tuples/records are not aliased like SQL expressions
When it happens
Trigger: Writing `select name = {first, last}` or `derive x = (a, b)` where the right-hand side is already a tuple and the left-hand side adds an alias to it.
Common situations: Refactoring a single column into a tuple but keeping the old alias, misunderstanding PRQL's tuple/record syntax versus SQL `AS` aliasing, or copy-pasting from other query languages.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- tuple: expected to have at least one entry when initial…
- `take`: expected early or late, found
- std.from_text: expected a string literal, found
- `format`: expected csv or json, found
- expected a table, found
AI-assisted analysis of PRQL/prql@e164e249b9 (2026-09-09).
Data as JSON: /api/errors/bfbdfbb3c4aa75ba.
Report an issue: GitHub.
Appendix: source
Thrown at prqlc/prqlc/src/semantic/resolver/transforms.rs:633
partition: None,
frame: WindowFrame::default(),
sort: Vec::new(),
};
let ty = self.infer_type_of_special_func(&transform_call)?;
Ok(Expr {
ty,
..Expr::new(ExprKind::TransformCall(transform_call))
})
}
/// Wraps non-tuple Exprs into a singleton Tuple.
pub(super) fn coerce_into_tuple(&mut self, expr: Expr) -> Result<Expr> {
let is_tuple_ty =
expr.ty.as_ref().is_some_and(|t| t.kind.is_tuple()) && !expr.kind.is_all();
Ok(if is_tuple_ty {
// a helpful check for a common anti-pattern
if let Some(alias) = expr.alias {
return Err(Error::new(Reason::Unexpected {
found: format!("assign to `{alias}`"),
})
.push_hint(format!("move assign into the tuple: `[{alias} = ...]`"))
.with_span(expr.span));
}
expr
} else {
let span = expr.span;
let mut expr = Expr::new(ExprKind::Tuple(vec![expr]));
expr.span = span;
self.fold_expr(expr)?
})
}
/// Figure out the type of a function call, if this function is a *special function*.
/// (declared in std module & requires special handling).View on GitHub (pinned to e164e249b9)