payloadcms/payload · error · ValidationError
Value must be unique
Error message
Value must be unique
What it means
Thrown as a structured `ValidationError` by `handleError` when MongoDB returns a duplicate-key error (E11000). The handler extracts the offending field from `error.keyValue` (or parses the message), strips any locale suffix from the path, and re-throws a per-field validation error so the API responds with a clean validation failure instead of a 500. The message resolves to `req.t('error:valueMustBeUnique')` when a translator is present, else the literal 'Value must be unique'.
Source
Thrown at packages/db-mongodb/src/utilities/handleError.ts:66
if (!error || typeof error !== 'object') {
throw error
}
// Handle uniqueness error from MongoDB
if ('code' in error && error.code === 11000) {
let path: null | string = null
if ('keyValue' in error && error.keyValue && typeof error.keyValue === 'object') {
path = Object.keys(error.keyValue)[0] ?? ''
} else if ('message' in error && typeof error.message === 'string') {
path = extractFieldFromMessage(error.message)
}
if (path) {
path = stripLocaleFromPath(path, req)
}
throw new ValidationError(
{
collection,
errors: [
{
message: req?.t ? req.t('error:valueMustBeUnique') : 'Value must be unique',
path: path ?? '',
},
],
global,
},
req?.t,
)
}
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw error
}
View on GitHub (pinned to 00c58b35c0)
Solutions
- Choose a different value for the flagged field (path is in the error).
- Pre-check uniqueness before writing when contention is likely.
- Remove the `unique: true` flag or the underlying unique index if duplicates are acceptable.
- Surface the ValidationError to the UI and prompt the user to change the value.
Example fix
// before
await payload.create({ collection: 'users', data: { email: 'taken@x.com' } })
// after
const exists = await payload.find({ collection: 'users', where: { email: { equals: 'taken@x.com' } }, limit: 1 })
if (exists.docs.length) throw new Error('email taken')
await payload.create({ collection: 'users', data: { email: 'new@x.com' } }) Defensive patterns
Strategy: try-catch
Validate before calling
async function assertUnique(payload, collection, field, value) {
const { docs } = await payload.find({ collection, where: { [field]: { equals: value } }, limit: 1 })
if (docs.length) throw new Error(`${field} already in use`)
} Type guard
const isValidationError = (e) => e?.name === 'ValidationError' && Array.isArray(e.data?.errors)
Try / catch
try { await payload.create({ collection, data }) }
catch (e) {
if (isValidationError(e) && e.data.errors.some(x => /unique/i.test(x.message))) {
return { ok: false, field: e.data.errors[0].path }
}
throw e
} Prevention
- Pre-check uniqueness for high-contention unique fields before writing.
- Catch ValidationError at the API boundary and map path -> user message.
- Avoid seeding duplicate values for unique-indexed fields.
- Re-evaluate whether `unique: true` is needed if duplicates are acceptable.
When it happens
Trigger: A `create` or `update` (or REST POST/PATCH) sets a field that carries a unique index to a value that already exists in the collection. The unique constraint can come from `unique: true` on a field or a manually-created unique index.
Common situations: Unique field collision on email/username/slug; seeding duplicate data; a race where two concurrent writes pick the same value; a previously-deleted doc whose unique value was retained by a soft-delete or a stale index.
Related errors
- Value must be unique
- The following field is invalid: ${name}
- Invalid template given
- Invalid database type given. Valid types are: ${Object.value
- No version or tag "${version}" found for package: ${packageN
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/e3b34ed8f6a163f0.
Report an issue: GitHub.