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 as a `Forbidden` error when a document create/update tries to consume a staged upload that belongs to a different collection or a different user than the one making the request. The staged upload JWT embeds `collectionSlug` and `user`; both must match the current request.
Source
Thrown at packages/payload/src/uploads/stagedUpload.ts:168
collectionSlug: string
req: PayloadRequest
uploadReference: unknown
}): Promise<File> => {
if (
!uploadReference ||
typeof uploadReference !== 'object' ||
Array.isArray(uploadReference) ||
!('uploadId' in uploadReference) ||
typeof uploadReference.uploadId !== 'string'
) {
throw new APIError('Invalid staged upload.', 400)
}
const { uploadId } = uploadReference
const upload = await verifyUploadID(req, uploadId)
if (upload.collectionSlug !== collectionSlug || upload.user !== getUser(req)) {
throw new Forbidden(req.t)
}
const directory = await getUploadDirectory(req, upload.collectionSlug)
const tempFilePath = path.join(directory, upload.id)
let data: Buffer
try {
data = await fs.readFile(tempFilePath)
} catch {
throw new APIError('Staged upload was not found.', 400)
}
if (data.length !== upload.filesize) {
await fs.rm(tempFilePath, { force: true })
throw new APIError('Staged upload is incomplete.', 400)
}
await fs.rm(tempFilePath, { force: true })View on GitHub (pinned to 00c58b35c0)
Solutions
- Ensure the same authenticated session that called `generateStagedUploadInstructions` also submits the create/update request.
- Confirm the collection slug in the upload instructions matches the collection slug of the target document operation.
- Do not reuse uploadIds across users -- each user must stage their own upload.
- If impersonation or admin-on-behalf-of flows are needed, generate the instructions in the target user's context.
Example fix
// before -- instructions generated as user A, consumed as user B
const instr = await adminClient.generateInstructions({ collectionSlug: 'media', ... })
await userBClient.create({ collection: 'media', data: { file: { uploadReference: { uploadId: instr.file.uploadReference.uploadId } } } })
// after -- same session for both steps
const instr = await userClient.generateInstructions({ collectionSlug: 'media', ... })
await userClient.create({ collection: 'media', data: { file: { uploadReference: { uploadId: instr.file.uploadReference.uploadId } } } }) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the same user session and collection are used for staging and consuming
const userKey = `${req.user.collection}:${req.user.id}`
// The uploadId JWT embeds user and collectionSlug -- regenerate if either changes
if (sessionChanged) {
const fresh = await generateStagedUploadInstructions({ collectionSlug, req, ... })
} Try / catch
try {
await payload.create({ collection, data })
} catch (e) {
if (e.name === 'Forbidden') {
// re-authenticate or re-stage in the correct user context
} else throw e
} Prevention
- Keep the authentication token identical between staging and consuming.
- Do not share uploadIds across users or collections.
- If user context changes mid-flow, re-stage the upload.
When it happens
Trigger: The `uploadId` was generated for collection A but the create/update targets collection B; or the `uploadId` was generated by user X but user Y (or an anonymous request) submits the document that references it.
Common situations: Sharing an uploadId across users in a multi-tenant app; generating instructions for one collection but POSTing to another; a session changed (logout / re-login as different user) between staging and consuming the upload; the `getUser` key (`collection:id`) differs because the auth strategy changed.
Related errors
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- Upload collection ${upload.collectionSlug} was not found
- Uploaded file is larger than expected.
- Uploaded file size does not match the expected size.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/20bd31e3c5694a85.
Report an issue: GitHub.