PRQL/prql · error · Error

input to append by:name must have all columns defined

Error message

{name} input to append by:name must have all columns defined

What it means

PRQL's native `append ... by:name` needs every column of both inputs to be individually known. If an input's lineage contains a wildcard `All` column (columns not yet expanded, e.g. from `from tbl` without a `select`), the compiler cannot perform by-name matching and throws this error. A hint suggests adding a `select` earlier in the pipeline.

Solutions

  1. Add a `select [col1, col2, ...]` before `append ... by:name` on the input that has undefined columns.
  2. List all columns explicitly on both sides so names are known to the compiler.
  3. Use positional `append` (no `by:`) if exact column lists are impractical.
  4. Replace `select *`-style usage with an explicit column list.

Example fix

// before
from employees | append managers by:name
// after
from employees | select [id, name] | append (from managers | select [id, name]) by:name
Defensive patterns

Strategy: validation

Validate before calling

// Require an explicit select before every by:name append
function requireSelectBeforeByName(prql) {
  const idx = prql.search(/append\s+\S+\s+by:name/);
  if (idx >= 0 && !/select\s+\[[^\]]+\]\s*\|\s*append/.test(prql)) {
    throw new Error('add an explicit select [cols...] before append by:name');
  }
}

Type guard

function hasExplicitSelect(pipeline) {
  return /\|\s*select\s+\[[^\]]+\]/.test(pipeline);
}

Try / catch

try {
  const sql = prqlc.compile(query);
} catch (e) {
  if (e.message.includes('must have all columns defined')) {
    // auto-insert a select of known columns before the offending input
  } else throw e;
}

Prevention

When it happens

Trigger: Running `from employees | append managers by:name` where either side still has a star/wildcard column set (no `select`), so the lineage contains a `LineageColumn::All` entry.

Common situations: Using `by:name` directly on raw `from table` inputs; forgetting to narrow columns before the append; assuming PRQL auto-expands `table.*` for by-name unions.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of PRQL/prql@e164e249b9 (2026-09-09). Data as JSON: /api/errors/a24923639f49f0fa. Report an issue: GitHub.

Appendix: source

Thrown at prqlc/prqlc/src/semantic/resolver/transforms.rs:277

                        }
                    }
                };

                // 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!(
                                "{name} input to append by:name must not have any unnamed columns"
                            )))
                            .with_span(rel.span)),
                            _ => Ok(()),
                        })?;
                    }

                    return Ok(new_binop(bottom, &["std", "_append_by_name"], top));
                } else {

View on GitHub (pinned to e164e249b9)