Skip to content

[plugin-ecommerce] validateOptions hook fails when trash is disabled on Variants collection #16367

@jhb-dev

Description

@jhb-dev

Describe the Bug

The validateOptions hook on the options field of the Variants collection (added by @payloadcms/plugin-ecommerce) queries sibling variants using a deletedAt filter:

// packages/plugin-ecommerce/src/collections/variants/createVariantsCollection/hooks/validateOptions.ts
const product = await req.payload.findByID({
  id: productID,
  collection: productsCollectionSlug,
  depth: 1,
  joins: {
    variants: {
      where: {
        deletedAt: { exists: false },
        ...(data.id && { id: { not_equals: data.id } }),
      },
    },
  },
  // ...
})

If a user disables trash on the Variants collection (e.g. via variantsCollectionOverride setting trash: false), the deletedAt field no longer exists on that collection. Payload's join-query sanitizer then rejects the query with:

QueryError: The following path cannot be queried: deletedAt

The variant create/update fails with a toast error in the admin UI, and the operation fails in the REST/Local APIs with status 400.

The deletedAt clause is redundant either way: when trash: true, Payload already excludes soft-deleted documents by default unless trash: true is explicitly passed to the query. So the fix is to drop the clause.

Suggested Fix

Remove the deletedAt clause from the where passed to the variants join in validateOptions:

joins: {
  variants: {
    where: {
      ...(data.id && { id: { not_equals: data.id } }),
    },
  },
},

Link to the code that reproduces this issue

https://github.com/jhb-dev/payload-ecommerce-variants-trash-validate

Reproduction Steps

  1. Clone the reproduction repository and run the development server (pnpm install && pnpm dev).
  2. The onInit seed (src/seed.ts) creates a product with two variant types and then attempts to create a variant, which triggers the validateOptions hook.
  3. The Variants collection is overridden in src/payload.config.ts with trash: false:
    products: {
      variants: {
        variantsCollectionOverride: ({ defaultCollection }) => ({
          ...defaultCollection,
          trash: false,
        }),
      },
    },
  4. Expected: the variant is created successfully.
  5. Actual: the create operation throws QueryError: The following path cannot be queried: deletedAt and the operation aborts.

Captured server log:

INFO: Seeding product + variant types/options...
INFO: Attempting to create a variant (this triggers the bug)...
ERROR: Variant creation failed (reproduced bug)
    err: {
      "type": "QueryError",
      "message": "The following path cannot be queried: deletedAt",
      "stack":
          QueryError: The following path cannot be queried: deletedAt
              at validateQueryPaths (.../payload/dist/database/queryValidation/validateQueryPaths.js:69:19)
              at sanitizeJoinQuery (.../payload/dist/database/sanitizeJoinQuery.js:90:5)
              at findByIDOperation (.../payload/dist/collections/operations/findByID.js:65:32)
              at validateOptions (.../@payloadcms/plugin-ecommerce/dist/collections/variants/createVariantsCollection/hooks/validateOptions.js:17:25)
              ...
      "data": [ { "path": "deletedAt" } ],
    }

Which area(s) are affected?

plugin: ecommerce

Environment Info

Binaries:
  Node: 24.3.0
  npm: 11.4.2
  pnpm: 10.33.0
Relevant Packages:
  payload: 3.84.0
  @payloadcms/plugin-ecommerce: 3.84.0
  next: 15.4.11
  @payloadcms/db-mongodb: 3.84.0
  @payloadcms/graphql: 3.84.0
  @payloadcms/next/utilities: 3.84.0
  @payloadcms/richtext-lexical: 3.84.0
  @payloadcms/translations: 3.84.0
  @payloadcms/ui/shared: 3.84.0
  react: 19.2.1
  react-dom: 19.2.1
Operating System:
  Platform: darwin
  Arch: arm64

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions