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.ts reads version data — if the upload relationship row doesn't exist in the _rels table, the property is not set on the result object (it becomes undefined, not null)
restoreVersion passes this version data to the write transform
- The write transform's
traverseFields receives undefined for the field
=== null fails → 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 an upload field (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?
Environment Info
- Payload v2:
@payloadcms/db-postgres@0.8.7 (confirmed affected)
- Payload v3:
packages/drizzle latest 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.
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 catchundefined. When a version is restored and the upload/relationship field was never set, the field value isundefined(notnull), so the relationship row is never added torelationshipsToDeleteand remains in the database.Affected Code (2 locations)
Line ~551 (localized fields):
Line ~572 (non-localized fields):
Fix
Change
=== nullto== null(loose equality) to also catchundefined:Why
undefinedoccurstransform/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 transformtraverseFieldsreceivesundefinedfor the field=== nullfails → relationship row is not scheduled for deletion → stale data persistsLink 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 usingdb-postgres(or any drizzle-based adapter) with versioned collections containing upload or relationship fields.Reproduction Steps
versions: { drafts: true }and anuploadfield (e.g., image)This also affects
relationshipfields, not justuploadfields.Which area(s) are affected?
Environment Info
@payloadcms/db-postgres@0.8.7(confirmed affected)packages/drizzlelatest main branch (confirmed same code exists at lines ~551, ~572)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.