gatsbyjs/gatsby · error · Error

Range filter ${predicate} should not have value of type ${ty

Error message

Range filter ${predicate} should not have value of type ${typeof value}

What it means

Same `resolveRangeEdge` path: a range predicate's value is a non-null object (not a primitive). LMDB keys cannot represent arbitrary objects, so the planner rejects it with the value's `typeof`.

Source

Thrown at packages/gatsby/src/datastore/lmdb/query/filter-using-index.ts:650

  predicate: DbComparator,
  edge: ValueEdges = ValueEdges.EQ
): IndexFieldValue | RangeEdgeBefore | RangeEdgeAfter | undefined {
  const fieldQueries = getFieldQueries(context.dbQueries, indexField)
  for (const dbQuery of fieldQueries) {
    if (context.usedQueries.has(dbQuery)) {
      continue
    }
    const filterStatement = getFilterStatement(dbQuery)
    if (filterStatement.comparator !== predicate) {
      continue
    }
    context.usedQueries.add(dbQuery)
    const value = filterStatement.value
    if (Array.isArray(value)) {
      throw new Error(`Range filter ${predicate} should not have array value`)
    }
    if (typeof value === `object` && value !== null) {
      throw new Error(
        `Range filter ${predicate} should not have value of type ${typeof value}`
      )
    }
    if (edge === 0) {
      return value
    }
    return edge < 0 ? getValueEdgeBefore(value) : getValueEdgeAfter(value)
  }
  return undefined
}

/**
 * Returns the edge after the given value, suitable for lmdb range queries.
 *
 * Example:
 * Get all items from index starting with ["foo"] prefix up to the next existing prefix:
 *
 * ```js

View on GitHub (pinned to 8b06340921)

Solutions

  1. Flatten the filter value to a scalar for range predicates.
  2. Inspect the filter object structure to ensure each comparator maps to a primitive.

Example fix

// before
filter: { age: { gt: { value: 10 } } }
// after
filter: { age: { gt: 10 } }
Defensive patterns

Strategy: validation

Validate before calling

function validateRangeFilters(filter) {
  for (const ops of Object.values(filter)) {
  for (const [op, value] of Object.entries(ops)) {
  if (["lt", "lte", "gt", "gte"].includes(op) && typeof value === "object" && value !== null && !Array.isArray(value)) {
  throw new Error(`Range ${op} must be primitive, got object`)
  }
  }
  }
}

Type guard

function isPrimitiveRangeValue(v: unknown): v is string | number | boolean | null {
  return v === null || ["string", "number", "boolean"].includes(typeof v)
}

Prevention

When it happens

Trigger: `filter: { age: { gt: { value: 10 } } }` (object instead of scalar) on an indexed range field.

Common situations: Nested filter objects accidentally passed through; double-wrapped filter builders.

Related errors


AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13). Data as JSON: /api/errors/0d10430dabe72dcf. Report an issue: GitHub.