payloadcms/payload · warning · Locked

Global document with slug "${globalSlug}" is currently locke

Error message

Global document with slug "${globalSlug}" is currently locked by another user and cannot be modified.

What it means

A `Locked` error thrown by `checkDocumentLockStatus` when a global is being modified without holding the lock. Another user lock entry exists for this `globalSlug` with a different `user.value` and an `updatedAt` within the configured lock duration.

Source

Thrown at packages/payload/src/utilities/checkDocumentLockStatus.ts:94

    // If there's a locked document, check lock conditions
    const lockedDoc = lockedDocumentResult?.docs[0]
    if (lockedDoc) {
      const lastEditedAt = new Date(lockedDoc?.updatedAt).getTime()
      const now = new Date().getTime()

      const lockDuration =
        typeof lockDocumentsProp === 'object' ? lockDocumentsProp.duration : lockDurationDefault

      const lockDurationInMilliseconds = lockDuration * 1000
      const currentUserId = req.user?.id

      // document is locked by another user and the lock hasn't expired
      if (
        lockedDoc.user?.value !== currentUserId &&
        now - lastEditedAt <= lockDurationInMilliseconds
      ) {
        throw new Locked(finalLockErrorMessage)
      }
    }
  }

  // Perform the delete operation regardless of overrideLock status
  await payload.db.deleteMany({
    collection: lockedDocumentsCollectionSlug,
    // Not passing req fails on postgres
    req: payload.db.name === 'mongoose' ? undefined : req,
    where: lockedDocumentQuery,
  })
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Wait for the lock to expire or have the locking user release it by navigating away / saving.
  2. Pass `overrideLock: true` in the `updateGlobal` Local API call for automated/forced writes.
  3. Configure `lockDocuments: false` on the global config if locking is not desired for it.
  4. Reduce the lock `duration` if locks persist too long.

Example fix

// before
await payload.updateGlobal({
  slug: 'siteSettings',
  data: { title: 'New Site' },
  // blocked by another user active lock
})

// after
await payload.updateGlobal({
  slug: 'siteSettings',
  data: { title: 'New Site' },
  overrideLock: true,
})
Defensive patterns

Strategy: try-catch

Validate before calling

// Before updating a global, check for active locks
const locks = await payload.find({
  collection: 'payload-locked-documents',
  where: { globalSlug: { equals: globalSlug } },
  depth: 1,
})
const lock = locks.docs[0]
if (lock && lock.user?.value !== currentUserId && Date.now() - new Date(lock.updatedAt).getTime() < duration * 1000) {
  throw new Error('Global is locked')
}

Try / catch

try {
  await payload.updateGlobal({ slug: globalSlug, data, overrideLock: false })
} catch (e) {
  if (e.name === 'Locked') {
    // wait, notify user, or use overrideLock: true for automated writes
  } else throw e
}

Prevention

When it happens

Trigger: A write to a global (e.g. `payload.updateGlobal`) where `overrideLock` is `false`, the global `lockDocuments` is not `false`, and a recent lock entry exists in the `locked-documents` collection for this `globalSlug` owned by a different user.

Common situations: Two admin users editing the same global (e.g. site settings) concurrently; a stale lock from a user who closed the browser tab; automated global updates colliding with manual admin edits; long lock durations preventing timely automated writes.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/ef4f8fe8e5f31330. Report an issue: GitHub.