{"record":{"id":"c844ff3d5ac14213","repo":"payloadcms/payload","slug":"you-are-not-allowed-to-perform-this-action-c844ff","errorCode":null,"errorMessage":"You are not allowed to perform this action.","messagePattern":"You are not allowed to perform this action\\.","errorType":"http","errorClass":"Forbidden","httpStatus":403,"severity":"error","filePath":"packages/storage-r2/src/handleMultiPartUpload.ts","lineNumber":50,"sourceCode":"export const getHandleMultiPartUpload =\n  ({\n    access = defaultR2ClientUploadsAccess,\n    bucket,\n    collections,\n    useCompositePrefixes = false,\n  }: Args): PayloadHandler =>\n  async (req) => {\n    const params = Object.fromEntries(req.searchParams) as R2StorageMultipartUploadHandlerParams\n    const collectionSlug = params.collection\n    const filetype = params.fileType\n\n    const collectionConfig = collections[collectionSlug]\n    if (!collectionConfig) {\n      throw new APIError(`Collection ${collectionSlug} was not found in R2 Storage options`)\n    }\n\n    if (!(await access({ collectionSlug, req }))) {\n      throw new Forbidden(req.t)\n    }\n\n    const collectionPrefix = (typeof collectionConfig === 'object' && collectionConfig.prefix) || ''\n    const { fileKey, sanitizedFilename } = await resolveSignedURLKey({\n      collectionPrefix,\n      collectionSlug,\n      docPrefix: params.docPrefix ?? undefined,\n      filename: params.fileName,\n      req,\n      useCompositePrefixes,\n    })\n\n    const multipartId = params.multipartId\n    const multipartKey = params.multipartKey\n    const multipartNumber = parseInt(params.multipartNumber || '')\n\n    if (multipartId && multipartKey) {\n      const multipartUpload = bucket.resumeMultipartUpload(multipartKey, multipartId)","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/storage-r2/src/handleMultiPartUpload.ts#L32-L68","documentation":"Thrown as a Forbidden error (HTTP 403) from the R2 multipart-upload handler after the access callback returns false. The access callback defaults to defaultR2ClientUploadsAccess, which delegates to the collection's config.access.create policy, falling back to a logged-in user check.","triggerScenarios":"An authenticated (or anonymous) user initiates a multipart upload and the access function resolves to false: either config.access.create returned false, or no access function and req.user is null, or a custom access function denied the request.","commonSituations":"Public/unauthenticated upload attempt on a collection whose create access requires a user; a logged-in user whose role lacks create permission; a custom access function with a bug that returns falsy for valid users; access policy depends on req.user data that is missing.","solutions":["Ensure the requester is authenticated (session cookie / token present) before initiating the multipart upload.","Review the collection's config.access.create policy and confirm it permits the user's role.","If public uploads are intended, set an access function on the R2 adapter that returns true for the relevant collection.","Pass a custom `access` callback to the R2 adapter that reflects the intended policy instead of the default."],"exampleFix":"// before — default access requires a logged-in user\nconst r2Adapter = new R2Storage({ bucket, collections: { media: {} } })\n\n// after — allow public uploads to 'media' only\nconst r2Adapter = new R2Storage({\n  bucket,\n  collections: { media: {} },\n  access: async ({ collectionSlug, req }) =>\n    collectionSlug === 'media' ? true : Boolean(req.user),\n})","handlingStrategy":"try-catch","validationCode":"// Before initiating, ensure the user is authenticated\nif (!user) {\n  throw new Error('Sign in required to upload files.')\n}\n// Optionally call the collection's create access locally if accessible\n","typeGuard":"import { Forbidden } from 'payload'\n\nfunction isForbidden(err: unknown): err is Forbidden {\n  return err instanceof Forbidden\n}","tryCatchPattern":"try {\n  await initiateMultipartUpload({ collection: slug })\n} catch (err) {\n  if (err instanceof Forbidden) {\n    // redirect to login or show 'permission denied'\n    redirectToLogin()\n    return\n  }\n  throw err\n}","preventionTips":["Verify the user session is valid before showing the upload UI.","Mirror the collection's create-access policy in the client to disable the upload control preemptively.","Provide a custom access callback with clear, debuggable logic rather than relying on the default."],"tags":["r2-storage","upload","access-control","forbidden","auth"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}