{"record":{"id":"651014ae0e1a01a3","repo":"payloadcms/payload","slug":"error-notallowedtoperformaction-651014","errorCode":null,"errorMessage":"error:notAllowedToPerformAction","messagePattern":"error:notAllowedToPerformAction","errorType":"exception","errorClass":"Forbidden","httpStatus":403,"severity":"error","filePath":"packages/payload/src/auth/operations/verifyEmail.ts","lineNumber":22,"sourceCode":"import type { PayloadRequest } from '../../types/index.js'\n\nimport { APIError, Forbidden } from '../../errors/index.js'\nimport { appendNonTrashedFilter } from '../../utilities/appendNonTrashedFilter.js'\nimport { commitTransaction } from '../../utilities/commitTransaction.js'\nimport { initTransaction } from '../../utilities/initTransaction.js'\nimport { killTransaction } from '../../utilities/killTransaction.js'\n\nexport type Args = {\n  collection: Collection\n  req: PayloadRequest\n  token: string\n}\n\nexport const verifyEmailOperation = async (args: Args): Promise<boolean> => {\n  const { collection, req, token } = args\n\n  if (collection.config.auth.disableLocalStrategy) {\n    throw new Forbidden(req.t)\n  }\n  if (!Object.prototype.hasOwnProperty.call(args, 'token')) {\n    throw new APIError('Missing required data.', httpStatus.BAD_REQUEST)\n  }\n\n  try {\n    const shouldCommit = await initTransaction(req)\n\n    const where = appendNonTrashedFilter({\n      enableTrash: Boolean(collection.config.trash),\n      trash: false,\n      where: {\n        _verificationToken: { equals: token },\n      },\n    })\n\n    const user = await req.payload.db.findOne<any>({\n      collection: collection.config.slug,","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/auth/operations/verifyEmail.ts#L4-L40","documentation":"Thrown at the top of `verifyEmailOperation` when `collection.config.auth.disableLocalStrategy` is true. Email verification is a local-strategy feature, so it is refused for external-strategy collections. `Forbidden` (HTTP 403).","triggerScenarios":"A `GET/POST /api/<collection>/verify/<token>` (or Local API `payload.verifyEmail`) is invoked on a collection with `auth: { disableLocalStrategy: true }` and the guard fires before token lookup.","commonSituations":"Verification links sent for an SSO collection; a shared email-verification pipeline that doesn't branch on strategy; misconfiguration mixing `verify` with `disableLocalStrategy`.","solutions":["Only call verifyEmail on local-strategy collections (`auth.disableLocalStrategy: false`).","For external-strategy collections, handle email verification in the IdP.","If you intend local email verification, ensure `auth.verify` is set and `disableLocalStrategy` is false."],"exampleFix":"// before\nawait payload.verifyEmail({ collection, token, req })\n// after\nif (!collectionConfig.auth.disableLocalStrategy) {\n  await payload.verifyEmail({ collection, token, req })\n}","handlingStrategy":"validation","validationCode":"// Skip verifyEmail for external-strategy collections\nif (collection.config.auth.disableLocalStrategy) {\n  throw new Error('Email verification is unavailable for this collection')\n}\nawait payload.verifyEmail({ collection, token, req })","typeGuard":"function supportsEmailVerification(cfg: CollectionConfig): boolean {\n  return !cfg.auth?.disableLocalStrategy && !!cfg.auth?.verify\n}","tryCatchPattern":"if (collection.config.auth.disableLocalStrategy) {\n  // verification handled by the IdP\n} else {\n  await payload.verifyEmail({ collection, token, req })\n}","preventionTips":["Branch verification flows on `disableLocalStrategy`.","Only enable `auth.verify` on local-strategy collections.","Document which collections perform their own email verification."],"tags":["auth","verify-email","sso","config"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}