payloadcms/payload · warning · Locked
Document with ID ${id} is currently locked by another user a
Error message
Document with ID ${id} is currently locked by another user and cannot be modified. What it means
A `Locked` error thrown by `checkDocumentLockStatus` when a collection document is being modified by a request that does not hold the lock. Another user lock entry exists in the `locked-documents` collection, the lock holder is a different user, and the lock has not expired (within `lockDocuments.duration` seconds, default 300).
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
- Wait for the lock to expire (default 300s / 5 min) or have the locking user close the document.
- Pass `overrideLock: true` in the Local API call if the operation should force through (use with caution).
- Reduce `lockDocuments.duration` if locks linger too long.
- Use `payload.db.deleteMany` on the `locked-documents` collection to manually clear a stuck lock if needed.
Example fix
// before
await payload.update({
collection: 'posts',
id,
data: { title: 'New' },
// overrideLock defaults to true in Local API, but admin UI sets false
})
// after -- explicitly override when appropriate
await payload.update({
collection: 'posts',
id,
data: { title: 'New' },
overrideLock: true,
}) Defensive patterns
Strategy: try-catch
Validate before calling
// Before updating, check if the document is locked by another user
const locks = await payload.find({
collection: 'payload-locked-documents',
where: {
and: [
{ 'document.relationTo': { equals: collectionSlug } },
{ 'document.value': { equals: id } },
],
},
depth: 1,
})
const lock = locks.docs[0]
if (lock && lock.user?.value !== currentUserId && Date.now() - new Date(lock.updatedAt).getTime() < 300000) {
throw new Error('Document is locked')
} Try / catch
try {
await payload.update({ collection, id, data, overrideLock: false })
} catch (e) {
if (e.name === 'Locked') {
// wait, notify user, or pass overrideLock: true if appropriate
} else throw e
} Prevention
- In collaborative UIs, show lock status and warn before editing a locked document.
- Use overrideLock: true only for intentional force-overrides.
- Set a reasonable lockDocuments.duration (default 300s).
- Release locks explicitly when a user navigates away if possible.
When it happens
Trigger: A PATCH/DELETE on a collection document where `overrideLock` is `false` (default for the admin UI), `lockDocuments` is not `false`, and a recent `locked-documents` entry exists with a different `user.value` and an `updatedAt` within the lock duration window.
Common situations: Two admin users editing the same document concurrently; a user navigated away without saving, leaving a stale lock that has not expired yet; an automated script modifies a document a user currently has open in the admin panel; the lock duration is set very long and locks are not being released.
Related errors
- Global document with slug "${globalSlug}" is currently locke
- ${sanitizedConfig.admin!.user} is not a valid admin user col
- Unauthorized, you must be logged in to make this request.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/72558e1b01d14eef.
Report an issue: GitHub.