PRQL/prql · error · Error

parameter `range`: expected a range, found

Error message

parameter `range`: expected a range, found {found}

What it means

The `range` parameter of the `window` transform expects a range (tuple of two bounds) over the window's ordering value, analogous to SQL `RANGE BETWEEN`. When `try_restrict_range` cannot reduce the argument to a range, the resolver throws this error printing the offending expression.

Solutions

  1. Pass a range tuple, e.g. `range:(0..10)`
  2. Use `..` range syntax with explicit bounds instead of brackets
  3. Use `rows:` if the frame should be counted in rows rather than value distance

Example fix

// before
window range:[1,10] (sum amount)
// after
window range:(1..10) (sum amount)
Defensive patterns

Strategy: validation

Validate before calling

const range = "(0..10)"; // must be a PRQL range tuple
if (!range.startsWith("(") || !range.includes("..")) {
  throw new Error("range must be a tuple like (start..end)");
}

Type guard

const isRangeArg = (v) => typeof v === "object" && v !== null && "start" in v && "end" in v;

Try / catch

try { prql_compile(query); } catch (e) { if (e.message.includes("parameter `range`")) { /* convert arg to a range tuple */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling `window range:<x>` where `<x>` is not a range tuple — e.g. `range:5`, `range:[1,10]`, an unquoted expression, or a range over non-literal bounds.

Common situations: Using list/bracket syntax instead of `..` range syntax; porting SQL `RANGE BETWEEN` directly; mixing up `range:` and `rows:` parameter semantics.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

                        .with_span(rolling.span)
                    })?
                };

                let rows = {
                    let range_tuple = try_restrict_range(rows).map_err(|expr| {
                        Error::new(Reason::Expected {
                            who: Some("parameter `rows`".to_string()),
                            expected: "a range".to_string(),
                            found: write_pl(expr.clone()),
                        })
                        .with_span(expr.span)
                    })?;
                    into_literal_range(range_tuple)?
                };

                let range = {
                    let range_tuple = try_restrict_range(range).map_err(|expr| {
                        Error::new(Reason::Expected {
                            who: Some("parameter `range`".to_string()),
                            expected: "a range".to_string(),
                            found: write_pl(expr.clone()),
                        })
                        .with_span(expr.span)
                    })?;
                    into_literal_range(range_tuple)?
                };

                let (kind, start, end) = if expanding {
                    (WindowKind::Rows, None, Some(0))
                } else if rolling > 0 {
                    (WindowKind::Rows, Some(-rolling + 1), Some(0))
                } else if !range_is_empty(&rows) {
                    (WindowKind::Rows, rows.0, rows.1)
                } else if !range_is_empty(&range) {
                    (WindowKind::Range, range.0, range.1)
                } else {

View on GitHub (pinned to e164e249b9)