PRQL/prql · error · Error
` `: expected relation, found
Error message
`{name}`: expected relation, found {found} What it means
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.
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.
Example fix
// before from t | append (1 + 1) by:name // after from t | append (from other | select [a, b]) by:name
Defensive patterns
Strategy: validation
Validate before calling
// Ensure both append inputs are pipelines that start with `from`
function validateByNameAppend(prql) {
const parts = prql.split(/\|\s*append\s+/);
if (parts.length > 1 && /by:name/.test(prql)) {
for (const p of parts) {
if (!/(^|\(|\s)from\s/.test(p)) {
throw new Error('both inputs of append by:name must be table pipelines (from ...)');
}
}
}
} Type guard
function isTablePipeline(expr) {
return typeof expr === 'string' && /(^|[\s(])from\s+\w/.test(expr);
} Try / catch
try {
const sql = prqlc.compile(query);
} catch (e) {
if (e.message.includes(': expected relation, found')) {
// log which input lacks lineage; rewrite it as a from-pipeline
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- input to append by:name must have all columns defined
- parameter `expanding`: expected a boolean, found
- parameter `rolling`: expected a number, found
- `by`: expected position or name, found
- input to append by:name must not have any unnamed columns
AI-assisted analysis of PRQL/prql@e164e249b9 (2026-09-09).
Data as JSON: /api/errors/42c6f699d70f77ea.
Report an issue: GitHub.
Appendix: source
Thrown at prqlc/prqlc/src/semantic/resolver/transforms.rs:268
"\"position\"" => false,
"\"name\"" => true,
_ => {
return Err(Error::new(Reason::Expected {
who: Some("`by`".to_string()),
expected: "position or name".to_string(),
found: ident.to_string(),
})
.with_span(span))
}
}
};
// TODO: support database engine-level UNION ALL BY NAME in PR #6037
if by_name {
// input validation for PRQL-native implementation
for (name, rel) in [("top", top.clone()), ("bottom", bottom.clone())] {
let lineage = rel.lineage.clone().ok_or_else(|| {
Error::new(Reason::Expected {
who: Some(format!("`{name}`")),
expected: "relation".to_string(),
found: write_pl(rel.clone()),
})
.with_span(rel.span)
})?;
lineage.columns.iter().try_for_each(|c| match c {
LineageColumn::All { .. } => Err(Error::new(Reason::Simple(format!(
"{name} input to append by:name must have all columns defined"
)))
.push_hint("try adding a select earlier in the pipeline")
.with_span(rel.span)),
LineageColumn::Single {
name: None,
target_name: None,
..
} => Err(Error::new(Reason::Simple(format!(View on GitHub (pinned to e164e249b9)