{"record":{"id":"42c6f699d70f77ea","repo":"PRQL/prql","slug":"name-expected-relation-found-found","errorCode":null,"errorMessage":"`{name}`: expected relation, found {found}","messagePattern":"`(.+?)`: expected relation, found (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"prqlc/prqlc/src/semantic/resolver/transforms.rs","lineNumber":268,"sourceCode":"                        \"\\\"position\\\"\" => false,\n                        \"\\\"name\\\"\" => true,\n                        _ => {\n                            return Err(Error::new(Reason::Expected {\n                                who: Some(\"`by`\".to_string()),\n                                expected: \"position or name\".to_string(),\n                                found: ident.to_string(),\n                            })\n                            .with_span(span))\n                        }\n                    }\n                };\n\n                // TODO: support database engine-level UNION ALL BY NAME in PR #6037\n                if by_name {\n                    // input validation for PRQL-native implementation\n                    for (name, rel) in [(\"top\", top.clone()), (\"bottom\", bottom.clone())] {\n                        let lineage = rel.lineage.clone().ok_or_else(|| {\n                            Error::new(Reason::Expected {\n                                who: Some(format!(\"`{name}`\")),\n                                expected: \"relation\".to_string(),\n                                found: write_pl(rel.clone()),\n                            })\n                            .with_span(rel.span)\n                        })?;\n\n                        lineage.columns.iter().try_for_each(|c| match c {\n                            LineageColumn::All { .. } => Err(Error::new(Reason::Simple(format!(\n                                \"{name} input to append by:name must have all columns defined\"\n                            )))\n                            .push_hint(\"try adding a select earlier in the pipeline\")\n                            .with_span(rel.span)),\n                            LineageColumn::Single {\n                                name: None,\n                                target_name: None,\n                                ..\n                            } => Err(Error::new(Reason::Simple(format!(","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/PRQL/prql/blob/e164e249b99485890036eb60f57c37520379b240/prqlc/prqlc/src/semantic/resolver/transforms.rs#L250-L286","documentation":"When `append ... by:name` is compiled natively in PRQL, both inputs (`top` and `bottom` relations) must have resolvable lineage, i.e. PRQL must know their concrete columns. This error is thrown when one input's lineage is missing (its columns cannot be determined), so a by-name union cannot be computed.","triggerScenarios":"Calling `append other by:name` where either the top or bottom relation has no lineage — typically when the input is not a table-producing pipeline (e.g. the result of certain function calls, literals, or expressions that do not carry relation/column info).","commonSituations":"Appending a non-table expression; passing a scalar or literal to `append by:name`; using append on something produced by a function returning an unresolved relation; intermediate pipelines that discard column information.","solutions":["Ensure both sides of `append ... by:name` are full pipelines starting from a relation (e.g. `from x | select [...]`).","Add an explicit `select` to materialize columns on the offending input.","If you don't need by-name matching, use plain `append` (positional), which has weaker column requirements.","Check the input isn't wrapped in a function that returns a non-relation value."],"exampleFix":"// before\nfrom t | append (1 + 1) by:name\n// after\nfrom t | append (from other | select [a, b]) by:name","handlingStrategy":"validation","validationCode":"// Ensure both append inputs are pipelines that start with `from`\nfunction validateByNameAppend(prql) {\n  const parts = prql.split(/\\|\\s*append\\s+/);\n  if (parts.length > 1 && /by:name/.test(prql)) {\n    for (const p of parts) {\n      if (!/(^|\\(|\\s)from\\s/.test(p)) {\n        throw new Error('both inputs of append by:name must be table pipelines (from ...)');\n      }\n    }\n  }\n}","typeGuard":"function isTablePipeline(expr) {\n  return typeof expr === 'string' && /(^|[\\s(])from\\s+\\w/.test(expr);\n}","tryCatchPattern":"try {\n  const sql = prqlc.compile(query);\n} catch (e) {\n  if (e.message.includes(': expected relation, found')) {\n    // log which input lacks lineage; rewrite it as a from-pipeline\n  } else throw e;\n}","preventionTips":["Only append relations produced by `from` pipelines.","Never pass literals or scalar expressions to `append by:name`.","Keep functions used as append inputs returning full table pipelines."],"tags":["prql","append-transform","type-mismatch","lineage"],"backgroundTag":"type-mismatch","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"}