{"record":{"id":"bfbdfbb3c4aa75ba","repo":"PRQL/prql","slug":"unexpected-assign-to-alias-move-assign-into","errorCode":null,"errorMessage":"Unexpected: assign to `{alias}` (move assign into the tuple: `[{alias} = ...]`)","messagePattern":"Unexpected: assign to `(.+?)` \\(move assign into the tuple: `\\[(.+?) = \\.\\.\\.\\]`\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"prqlc/prqlc/src/semantic/resolver/transforms.rs","lineNumber":633,"sourceCode":"            partition: None,\n            frame: WindowFrame::default(),\n            sort: Vec::new(),\n        };\n        let ty = self.infer_type_of_special_func(&transform_call)?;\n        Ok(Expr {\n            ty,\n            ..Expr::new(ExprKind::TransformCall(transform_call))\n        })\n    }\n\n    /// Wraps non-tuple Exprs into a singleton Tuple.\n    pub(super) fn coerce_into_tuple(&mut self, expr: Expr) -> Result<Expr> {\n        let is_tuple_ty =\n            expr.ty.as_ref().is_some_and(|t| t.kind.is_tuple()) && !expr.kind.is_all();\n        Ok(if is_tuple_ty {\n            // a helpful check for a common anti-pattern\n            if let Some(alias) = expr.alias {\n                return Err(Error::new(Reason::Unexpected {\n                    found: format!(\"assign to `{alias}`\"),\n                })\n                .push_hint(format!(\"move assign into the tuple: `[{alias} = ...]`\"))\n                .with_span(expr.span));\n            }\n\n            expr\n        } else {\n            let span = expr.span;\n            let mut expr = Expr::new(ExprKind::Tuple(vec![expr]));\n            expr.span = span;\n\n            self.fold_expr(expr)?\n        })\n    }\n\n    /// Figure out the type of a function call, if this function is a *special function*.\n    /// (declared in std module & requires special handling).","sourceCodeStart":615,"sourceCodeEnd":651,"githubUrl":"https://github.com/PRQL/prql/blob/e164e249b99485890036eb60f57c37520379b240/prqlc/prqlc/src/semantic/resolver/transforms.rs#L615-L651","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nselect name = {first, last}\n// after\nselect {first, last}","handlingStrategy":"validation","validationCode":"// when generating select/derive args, reject aliasing a whole tuple\nif (arg.alias && arg.expr.kind === 'tuple') throw new Error('move assign inside the tuple: [' + arg.alias + ' = ...]');","typeGuard":"const aliasesTuple = (arg) => Boolean(arg.alias) && Array.isArray(arg.expr);","tryCatchPattern":"try { compile(prql) } catch (e) { if (e.message.includes('Unexpected: assign to')) { /* restructure the select/derive argument */ } }","preventionTips":["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"],"tags":["prql","compile-time","anti-pattern","aliasing"],"backgroundTag":"invalid-argument-format","analyzedSha":"e164e249b99485890036eb60f57c37520379b240","analyzedAt":"2026-09-09T12:18:38.727Z","contentChangedAt":"2026-09-09T12:18:38.727Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}