Skip to content

[BUG]: rc.5 RQB v2 throws "Unexpected 'undefined' in filter value" on undefined where-filter fields that rc.4 skipped (types still allow undefined) #6180

Description

@mikepage

What version of drizzle-orm are you using?

1.0.0-rc.5-169397b (the rc5 dist-tag snapshot; rc.4 is fine)

What version of drizzle-kit are you using?

1.0.0-rc.5-ab785fc (not involved)

Other packages

postgres 3.4.x (postgres-js driver), RQB v2 (defineRelations)

Describe the Bug

Between 1.0.0-rc.4 and the 1.0.0-rc.5-* snapshots, RQB v2 started throwing at runtime when a where filter object contains a field whose value is undefined:

Error: Unexpected 'undefined' in filter value. Use 'EmptyFilter' if you want the filter field to be skipped.
    at relationsFilterToSQL (...)
    at PgDialect.buildRelationalQuery (...)

Minimal repro:

const db = drizzle({ client, relations });

// rc.4: `name` is skipped, filters on the remaining fields.
// rc.5-169397b: throws "Unexpected 'undefined' in filter value."
await db.query.organizations.findMany({
  where: { name: undefined },
  limit: 1,
});

Two problems with this:

  1. The types still accept it. The filter field types are optional (name?: ...), so { name: cond ? value : undefined } type-checks cleanly and then explodes at runtime. If skipping-on-undefined is being removed deliberately, the types should reject undefined (exactOptionalPropertyTypes-style) so the break is caught at compile time — and EmptyFilter doesn't appear to be an exported symbol to migrate to.

  2. It breaks the established optional-filter idiom silently. where: { siteId, translationGroupId: maybeFilter ?? undefined } was the natural way to express optional filters under rc.4 (and matches how most JS query builders treat undefined). Every such call site now becomes a production 500 on upgrade, with no changelog entry (the rc5 snapshots have no release notes yet).

We hit this via Dependabot bumping to the rc5 snapshot: every list endpoint with an optional filter started returning 500s in e2e, e.g.

const translationGroupId =
  opts.translationGroupIsNull === true  ? { isNull: true as const } :
  opts.translationGroupIsNull === false ? { isNotNull: true as const } :
  undefined; // ← rc.4: skipped; rc.5: throws

await db.query.pages.findMany({
  where: { organizationId, siteId, translationGroupId },
  ...
});

If the new strictness is intentional, this issue is a request to (a) make the filter types reject undefined so it fails at compile time, (b) export/document EmptyFilter, and (c) call it out in the rc.5 release notes as breaking. If it's not intentional, undefined should keep meaning "skip this field" as in rc.4.

Environment

Node 24 / Cloudflare Workers (workerd), PostgreSQL, postgres-js driver. Reproduces identically under plain Node.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions