{"record":{"id":"14213f9f2e1bbeec","repo":"PRQL/prql","slug":"expected-an-identifier-found-found","errorCode":null,"errorMessage":"expected an identifier, found {found}","messagePattern":"expected an identifier, found (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"prqlc/prqlc/src/semantic/lowering.rs","lineNumber":793,"sourceCode":"                continue;\n            }\n\n            let id = e.target_id.unwrap();\n            match e.kind {\n                pl::ExprKind::Ident(_) if e.ty.as_ref().is_some_and(|x| x.kind.is_tuple()) => {\n                    res.extend(self.find_selected_all(e, None).with_span(except.span)?);\n                }\n                pl::ExprKind::Ident(ident) => {\n                    res.insert(\n                        self.lookup_cid(id, Some(&ident.name))\n                            .with_span(except.span)?,\n                    );\n                }\n                pl::ExprKind::All { within, except } => {\n                    res.extend(self.find_selected_all(*within, Some(*except))?)\n                }\n                _ => {\n                    return Err(Error::new(Reason::Expected {\n                        who: None,\n                        expected: \"an identifier\".to_string(),\n                        found: write_pl(e),\n                    }));\n                }\n            }\n        }\n        Ok(res)\n    }\n\n    fn declare_as_column(\n        &mut self,\n        mut expr_ast: pl::Expr,\n        is_aggregation: bool,\n    ) -> Result<rq::CId> {\n        // short-circuit if this node has already been lowered\n        if let Some(LoweredTarget::Compute(lowered)) = self.node_mapping.get(&expr_ast.id.unwrap())\n        {","sourceCodeStart":775,"sourceCodeEnd":811,"githubUrl":"https://github.com/PRQL/prql/blob/e164e249b99485890036eb60f57c37520379b240/prqlc/prqlc/src/semantic/lowering.rs#L775-L811","documentation":"Lowering converts resolved semantic expressions into SQL IR, and some constructs (like `std.all` expansion results) can only be lowered when they end up as plain identifiers. If `find_selected_all` or the expr lowering match reaches any expression kind that is not an identifier where one is required, this error is thrown.","triggerScenarios":"An `all` selection expansion (within/except) produces a non-identifier node that reaches the lowering path expecting a column name, e.g. complex expressions combined with `except` that do not reduce to identifiers.","commonSituations":"Using `all` / `except` with expressions like function calls or literals inside the selection, which cannot be named as columns in the output.","solutions":["Replace the offending expression with a plain column identifier","Expand the desired columns explicitly instead of relying on `all`/`except` for expression entries","Simplify the `except` list to contain only column names"],"exampleFix":"// before\nselect { all(), except { count() } }\n// after\nselect { all(), except { value } }  # except only column names","handlingStrategy":"validation","validationCode":"// Restrict all/except to plain column identifiers\nconst isIdent = (s) => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s);\nif (!except.every(isIdent)) throw new Error(\"except must list plain column names\");","typeGuard":"function isPlainIdentifier(node) {\n  return node && node.kind === \"ident\";\n}","tryCatchPattern":"try {\n  compile(query);\n} catch (e) {\n  if (e.message.includes(\"expected an identifier\")) {\n    console.error(\"all/except selections must expand to identifiers; expand columns manually.\");\n  } else { throw e; }\n}","preventionTips":["Use only column names inside all/except lists","Expand complex expressions manually instead of via `all`","Test selection expansions on small schemas first"],"tags":["prql","lowering","identifier"],"backgroundTag":"invalid-identifier","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"}