payloadcms/payload · error · Forbidden
You are not allowed to perform this action.
Error message
You are not allowed to perform this action.
What it means
Thrown by `getDuplicateDocumentData` when the source document is not found BUT a where-based read access policy is active. As with updateByID, Payload returns 403 rather than 404 to avoid revealing whether the document exists. The caller either lacks read access or the document is outside their where-clause scope.
Source
Thrown at packages/payload/src/duplicateDocument/index.ts:76
payload,
query: findOneArgs,
req,
})
if (selectedLocales && selectedLocales.length > 0 && duplicatedFromDocWithLocales) {
duplicatedFromDocWithLocales = filterDataToSelectedLocales({
configBlockReferences: payload.config.blocks,
docWithLocales: duplicatedFromDocWithLocales,
fields: collectionConfig.fields,
selectedLocales,
})
}
if (!duplicatedFromDocWithLocales && !hasWherePolicy) {
throw new NotFound(req.t)
}
if (!duplicatedFromDocWithLocales && hasWherePolicy) {
throw new Forbidden(req.t)
}
// remove the createdAt timestamp and rely on the db to set it
if ('createdAt' in duplicatedFromDocWithLocales) {
delete duplicatedFromDocWithLocales.createdAt
}
// remove the id and rely on the db to set it
if ('id' in duplicatedFromDocWithLocales) {
delete duplicatedFromDocWithLocales.id
}
duplicatedFromDocWithLocales = await beforeDuplicate({
id,
collection: collectionConfig,
context: req.context,
doc: duplicatedFromDocWithLocales,
overrideAccess: overrideAccess!,
req,View on GitHub (pinned to 00c58b35c0)
Solutions
- Confirm the caller has read access to the source document (run a scoped `findByID` first).
- If the operation is privileged/internal, use `overrideAccess: true` deliberately.
- Return a clear 'not allowed' message; do not leak existence by retrying different ids.
Example fix
// before
await payload.duplicate({ collection: 'posts', id, req, overrideAccess: false })
// after
const src = await payload.findByID({ collection: 'posts', id, req }) // enforces read access
if (!src) throw new ForbiddenError('cannot duplicate this post')
await payload.duplicate({ collection: 'posts', id, req }) Defensive patterns
Strategy: try-catch
Validate before calling
const src = await payload.findByID({ collection, id, req, depth: 0 }).catch(() => null)
if (!src) throw new ForbiddenError('Cannot duplicate this document')
await payload.duplicate({ collection, id, req }) Try / catch
try {
await payload.duplicate({ collection, id, req })
} catch (err) {
if (err instanceof Forbidden) return respond(403, 'Not allowed')
throw err
} Prevention
- Pre-check read access with a scoped findByID.
- Use `overrideAccess: true` only for deliberate privileged duplication.
- Return a uniform 403 to avoid leaking document existence.
When it happens
Trigger: A user whose `access.read` returns `{ where: {...} }` tries to duplicate a document outside that scope; cross-tenant duplication; duplicating a private doc as a non-owner.
Common situations: Multi-tenant read scoping; role-based access where users can only see their own docs; an admin tool duplicating across tenants without `overrideAccess`.
Related errors
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- You are not allowed to perform this action.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/3122da24ac356b9d.
Report an issue: GitHub.