payloadcms / payloadcms/payload

[plugin-multi-tenant] Race condition in auto-injected relationship field validation produces false "invalid relationship" errors

Open
#16,462 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

db: mongodb plugin: multi-tenant stale status: needs-triage v3
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

Describe the Bug

When a tenant-enabled collection is referenced from an array field on another tenant-enabled collection, creating a document with multiple rows that each reference a tenant-scoped item intermittently fails with per-row "This relationship field has an invalid relationship" validation errors — even though every referenced id exists, belongs to the correct tenant, and is otherwise valid.

The failure is non-deterministic and the rate scales with row count. With 8 rows against MongoDB it reproduces in roughly 1–4 of every 5 attempts; with more rows it approaches 100%.

Suspected root cause

The multi-tenant plugin auto-injects filterOptions on every relationship pointing at a tenant-enabled collection. Payload's relationship validator then runs validateFilterOptions, which calls payload.find to check the chosen id is in the allowed set.

Payload validates array rows in parallel via Promise.all. With N rows each holding a tenant-scoped relationship, N concurrent payload.find calls run on the same MongoDB session inside the create's transaction. MongoDB rejects all but one with:

MongoServerError: Given transaction number X does not match any in-progress transactions on session Y

validateFilterOptions swallows the error in its catch and returns an empty allow-list, so the field is reported as "invalid" for every row whose query lost the race.

This is structurally adjacent to #4078 / #4087 — that fix made validateFilterOptions honor the transaction, but didn't address N validators in the same transaction trying to use the session concurrently. The multi-tenant plugin makes this very easy to hit because it auto-injects filterOptions on every tenant-scoped relationship — the user's schema never declares one.

Link to the code that reproduces this issue

https://github.com/amitzur/payload-issue-multi-tenant-relationship-validation-race

Reproduction Steps

Run the reproducer script from the repo amitzur/payload-issue-multi-tenant-relationship-validation-race:

pnpm install
pnpm repro

Expected: the test passes — failures stays empty across all 5 trials.
Actual: the test fails — one or more trials produce per-row "This relationship field has an invalid relationship" errors.

The reproducer registers three collections via the multi-tenant plugin:

  • tenants
  • items (tenant-enabled)
  • holders (tenant-enabled, has rows: array of relationshipitems)

It then creates one tenant + 8 items in that tenant, then attempts five times to create a holder whose rows reference all 8 items. The expectation is that all five succeed; in practice one or more fail with per-row validation errors.

Race condition proof

Uncomment the following to make the repro pass: https://github.com/amitzur/payload-issue-multi-tenant-relationship-validation-race/blob/62ba2a89245934f7d9ec59f4df1a42db377af8e1/payload.config.ts#L74

Which area(s) are affected?

db: mongodb, plugin: multi-tenant

Environment Info
Node.js: v24.12.0
payload: 3.84.1
@payloadcms/plugin-multi-tenant: 3.84.1
@payloadcms/db-mongodb: 3.84.1
Next.js: 16.2.4

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running pnpm install and pnpm repro in the linked reproducer repository, then inspect payload.config.ts around line 74 and the relationship validation path described in the issue. Done means all five holder-creation trials succeed without per-row invalid-relationship errors, with failures remaining empty.

Written by the indexing model from the issue text.

Assessment

Tech stack
mongodb, typescript
Domain
backend, database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.