PRQL/prql · error · Error

input to append by:name must not have any unnamed columns

Error message

{name} input to append by:name must not have any unnamed columns

What it means

For `append ... by:name`, every column in each input must have a name, since columns are matched by name. This error is raised when an input contains an unnamed column (e.g. an anonymous expression column produced without an alias), making by-name matching impossible.

Solutions

  1. Alias every expression column before the append, e.g. `select [total = amount * 1.1]`.
  2. Ensure aggregates are named: `aggregate [total = sum amount]`.
  3. Inspect each input with a `select` listing all named columns.
  4. Fall back to positional `append` if names are irrelevant.

Example fix

// before
from t | select [amount * 1.1] | append other by:name
// after
from t | select [adjusted = amount * 1.1] | append other by:name
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every select item in by:name pipelines has an alias
function allSelectItemsNamed(prql) {
  const selects = prql.match(/select\s+\[([^\]]+)\]/g) || [];
  for (const s of selects) {
    for (const item of s.slice(s.indexOf('[') + 1, -1).split(',')) {
      const t = item.trim();
      if (t && !/^\w+$/.test(t) && !/\w\s*=/.test(t)) {
        throw new Error(`unnamed column in select: ${t}; alias it as name = ${t}`);
      }
    }
  }
}

Type guard

function isNamedSelectItem(item) {
  return /^\w+$/.test(item.trim()) || /^[\w.]+\s*=/.test(item.trim());
}

Try / catch

try {
  const sql = prqlc.compile(query);
} catch (e) {
  if (e.message.includes('must not have any unnamed columns')) {
    // flag the expression column and add an alias
  } else throw e;
}

Prevention

When it happens

Trigger: Appending with `by:name` where an input pipeline contains an expression column without an alias, such as `select [count]` on an aggregate without naming, or `derive`/`select` of an expression that yields `LineageColumn::Single { name: None }`.

Common situations: Selecting raw expressions (e.g. `select [amount * 1.1]`) without an alias before a by-name append; unnamed aggregate outputs; columns from functions that don't assign names.

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/3a479a4197863543. Report an issue: GitHub.

Appendix: source

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

                            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 {
                    (TransformKind::Append(Box::new(bottom)), top)
                }
            }
            "loop" => {
                let [pipeline, tbl] = unpack::<2>(func.args);

                let pipeline = self.fold_by_simulating_eval(pipeline, &tbl)?;

                (TransformKind::Loop(Box::new(pipeline)), tbl)

View on GitHub (pinned to e164e249b9)