-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
Describe the Bug
When restoring a published version that has no upload/relationship field value, the previously draft-saved upload/relationship data persists in the database and is not removed.
Root Cause: In packages/drizzle/src/transform/write/traverseFields.ts, the null check uses strict equality (=== null), which does not catch undefined. When a version is restored and the upload/relationship field was never set, the field value is undefined (not null), so the relationship row is never added to relationshipsToDelete and remains in the database.
Affected Code (2 locations)
Line ~551 (localized fields):
if (localeData === null) { // ❌ misses undefined
relationshipsToDelete.push({
locale: localeKey,
path: relationshipPath,
})
return
}Line ~572 (non-localized fields):
if (fieldData === null || (Array.isArray(fieldData) && fieldData.length === 0)) { // ❌ misses undefined
relationshipsToDelete.push({ path: relationshipPath })
return
}Fix
Change === null to == null (loose equality) to also catch undefined:
// localized
if (localeData == null) { // ✅ catches both null and undefined
// non-localized
if (fieldData == null || (Array.isArray(fieldData) && fieldData.length === 0)) { // ✅Why undefined occurs
transform/read/traverseFields.tsreads version data — if the upload relationship row doesn't exist in the_relstable, the property is not set on the result object (it becomesundefined, notnull)restoreVersionpasses this version data to the write transform- The write transform's
traverseFieldsreceivesundefinedfor the field === nullfails → relationship row is not scheduled for deletion → stale data persists
Link to the code that reproduces this issue
This is a bug in the core library code at packages/drizzle/src/transform/write/traverseFields.ts. The issue can be reproduced with any Payload project using db-postgres (or any drizzle-based adapter) with versioned collections containing upload or relationship fields.
Reproduction Steps
- Create a collection with
versions: { drafts: true }and anuploadfield (e.g., image) - Create a document without setting the image → Publish it
- Edit the document → Set an image → Save as Draft
- Click "Revert to published" (restore the published version that has no image)
- Expected: The image field should be empty (matching the published version)
- Actual: The image from the draft still appears and is saved to the database
This also affects relationship fields, not just upload fields.
Which area(s) are affected?
- db: postgres
Environment Info
- Payload v2:
@payloadcms/db-postgres@0.8.7(confirmed affected) - Payload v3:
packages/drizzlelatest main branch (confirmed same code exists at lines ~551, ~572) - Node.js: v22
- PostgreSQL: 15+
Additional Context
We discovered this bug in our production Payload v2 project and confirmed it also exists in v3's packages/drizzle/src/transform/write/traverseFields.ts. We have a working patch for v2 and are happy to submit a PR for v3 as well.