PRQL/prql · error · Error

`take`: expected early or late, found

Error message

`take`: expected early or late, found {ident}

What it means

The `take`-related dedup argument accepts only the literal values `early` or `late`, which control whether uniqueness is enforced before or after the take. Any other value passed where `early`/`late` is expected is rejected with this error at resolve time.

Solutions

  1. Use exactly `"early"` or `"late"` as the take argument literal.
  2. Check spelling and case; the comparison is against `"early"`/`"late"` strings.
  3. Remove the argument if you intended default behavior rather than a custom value.

Example fix

// before
uniq take: earyl
// after
uniq take: "early"
Defensive patterns

Strategy: validation

Validate before calling

const TAKE_MODES = ['"early"', '"late"'];
if (!TAKE_MODES.includes(mode)) throw new Error(`take mode must be "early" or "late", got ${mode}`);

Type guard

const isTakeMode = (v) => v === '"early"' || v === '"late"';

Try / catch

try { compile(prql) } catch (e) { if (e.message.includes('`take`: expected early or late')) { /* fix literal */ } }

Prevention

When it happens

Trigger: Calling `std.tuple_uniq` (the `take` argument) with anything other than the string literals `"early"` or `"late"` — e.g. a misspelled value, a non-literal expression, a variable, or quoting mistakes like `'EarLY'`.

Common situations: Typo in the flag (`earyl`, `last`), passing a dynamic/computed value instead of a literal, or copy-pasting SQL `DISTINCT`-style options into PRQL.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

                return Ok(Expr::new(ExprKind::Tuple(res)));
            }

            "tuple_uniq" => {
                let [take, list] = unpack::<2>(func.args);

                let take_late = {
                    let span = take.span;
                    let ident = take.clone().try_cast(
                        ExprKind::into_literal,
                        Some("`take`"),
                        "early or late",
                    )?;

                    match ident.to_string().as_str() {
                        "\"early\"" => false,
                        "\"late\"" => true,
                        _ => {
                            return Err(Error::new(Reason::Expected {
                                who: Some("`take`".to_string()),
                                expected: "early or late".to_string(),
                                found: ident.to_string(),
                            })
                            .with_span(span))
                        }
                    }
                };

                let list_items = list.kind.into_tuple().unwrap();

                log::trace!("tuple_uniq before: {list_items:#?}");

                let mut list_names: Vec<String> = Vec::new();
                let mut list_out: HashMap<String, Expr> = HashMap::new();

                for item in list_items {
                    let Some(name) = (match (&item.alias, &item.kind) {

View on GitHub (pinned to e164e249b9)