PRQL/prql · error · Error

expected a type, found

Error message

expected a type, found {decl}

What it means

When resolving a type expression (e.g. inside a function parameter's type annotation, `#` type position), PRQL resolves the identifier and requires the declaration to have a type (DeclKind::Ty). If the identifier names a function, column, or other non-type declaration, this error is thrown.

Solutions

  1. Use a valid type name from std (e.g. `int`, `text`, `date`, `bool`)
  2. Fix typos in the type annotation
  3. Ensure the identifier is not shadowed by a local function/variable of the same name

Example fix

// before
func add x:intger -> x + 1
// after
func add x:int -> x + 1
Defensive patterns

Strategy: validation

Validate before calling

const VALID_TYPES = ["int","float","bool","text","date","time","timestamp","column","table","relation","any"];
function validateTypeAnnotation(t) {
  if (!VALID_TYPES.includes(t)) throw new Error(`unknown type annotation: ${t}`);
}

Type guard

function isTypeName(id, knownTypes) {
  return knownTypes.includes(id.name);
}

Try / catch

try {
  compile(query);
} catch (e) {
  if (e.message.includes("expected a type, found")) {
    console.error("Type annotation must name a std type; check spelling and shadowing:", e.message);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Writing a type annotation that references a non-type declaration, e.g. `func f x:table -> ...` where `table` is not the type name expected, or annotating with a function/variable name by mistake.

Common situations: Typo in a type name (e.g. `text` vs `string` depending on std version), using a value name as a type, or importing shadowing a type name.

Related errors


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

Appendix: source

Thrown at prqlc/prqlc/src/semantic/resolver/expr.rs:28

use crate::Result;
use crate::{Error, Reason, Span, WithErrorInfo};

impl pl::PlFold for Resolver<'_> {
    fn fold_stmts(&mut self, _: Vec<pl::Stmt>) -> Result<Vec<pl::Stmt>> {
        unreachable!()
    }

    fn fold_type(&mut self, ty: Ty) -> Result<Ty> {
        Ok(match ty.kind {
            TyKind::Ident(ident) => {
                self.root_mod.module.shadow(NS_THIS);
                self.root_mod.module.shadow(NS_THAT);

                let fq_ident = self.resolve_ident(&ident)?;

                let decl = self.root_mod.module.get(&fq_ident).unwrap();
                let decl_ty = decl.kind.as_ty().ok_or_else(|| {
                    Error::new(Reason::Expected {
                        who: None,
                        expected: "a type".to_string(),
                        found: decl.to_string(),
                    })
                })?;
                let mut ty = decl_ty.clone();
                ty.name = ty.name.or(Some(fq_ident.name));

                self.root_mod.module.unshadow(NS_THIS);
                self.root_mod.module.unshadow(NS_THAT);

                ty
            }
            _ => pl::fold_type(self, ty)?,
        })
    }

    fn fold_var_def(&mut self, var_def: pl::VarDef) -> Result<pl::VarDef> {

View on GitHub (pinned to e164e249b9)