PRQL/prql · error · Error

expected , found

Error message

expected {expected_ty}, found {found_ty}

What it means

Generic type-check failure in the resolver: an expression's inferred type did not match the type expected by a transform or function argument. The `who` context usually names the offending construct (e.g. a `std.join` argument), and special hints are added when a function was found where a non-function type was expected (e.g. forgetting to call it).

Solutions

  1. Read `who` and the expected/found types in the full error; align the argument's type with the expected one.
  2. If a function was found where a value was expected, call it: add parentheses and arguments.
  3. Cast or convert the value (e.g. wrap numbers in `std.int` text conversions) when types are close but not identical.
  4. Check the docs for the transform's argument signature (e.g. `std.join` needs tables plus join keys).

Example fix

// before
join other (x -> x.id == id)
// after
join other (side:left, x.id == id)
Defensive patterns

Strategy: validation

Validate before calling

// check argument types against the transform signature before generating PRQL
if (!matchesExpectedType(arg.type, signature[transform][param])) throw new Error(`param ${param} of ${transform} must be ${signature[transform][param]}`);

Type guard

const isFunctionExpr = (e) => e.kind === 'func' || e.kind === 'lambda';
// if isFunctionExpr(arg) and a value is expected, the call is missing invocation

Try / catch

try { compile(prql) } catch (e) { if (e.message.includes('found') && e.message.includes('expected') && e.annotations?.who?.includes('std.join')) { /* fix join argument types */ } }

Prevention

When it happens

Trigger: Passing a wrong-typed argument to any transform — e.g. `join side:...` receiving a non-table, comparing incompatible types in `filter`, or giving a function object where a value/column type is required.

Common situations: Joining on a non-table expression, passing a lambda where a column is expected, mixing text and numeric literals in comparisons, or forgetting parentheses to invoke a table-returning function.

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


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

Appendix: source

Thrown at prqlc/prqlc/src/semantic/resolver/types.rs:205

{
    fn display_ty(ty: &Ty) -> String {
        if ty.name.is_none() {
            if let TyKind::Tuple(fields) = &ty.kind {
                if fields.len() == 1 && fields[0].is_wildcard() {
                    return "a tuple".to_string();
                }
            }
        }
        format!("type `{}`", write_ty(ty))
    }

    let who = who();
    let is_join = who
        .as_ref()
        .map(|x| x.contains("std.join"))
        .unwrap_or_default();

    let mut e = Error::new(Reason::Expected {
        who,
        expected: display_ty(expected),
        found: display_ty(found_ty),
    });

    if found_ty.kind.is_function() && !expected.kind.is_function() {
        let found = found_ty.kind.as_function().unwrap();
        let func_name = if let Some(func) = found {
            func.name_hint.as_ref()
        } else {
            None
        };
        let to_what = func_name
            .map(|n| format!("to function {n}"))
            .unwrap_or_else(|| "in this function call".to_string());

        e = e.push_hint(format!("Argument might be missing {to_what}?"));
    }

View on GitHub (pinned to e164e249b9)