payloadcms/payload · error · Forbidden

error:notAllowedToPerformAction

Error message

error:notAllowedToPerformAction

What it means

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).

Source

Thrown at packages/payload/src/auth/operations/verifyEmail.ts:22

import type { PayloadRequest } from '../../types/index.js'

import { APIError, Forbidden } from '../../errors/index.js'
import { appendNonTrashedFilter } from '../../utilities/appendNonTrashedFilter.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'

export type Args = {
  collection: Collection
  req: PayloadRequest
  token: string
}

export const verifyEmailOperation = async (args: Args): Promise<boolean> => {
  const { collection, req, token } = args

  if (collection.config.auth.disableLocalStrategy) {
    throw new Forbidden(req.t)
  }
  if (!Object.prototype.hasOwnProperty.call(args, 'token')) {
    throw new APIError('Missing required data.', httpStatus.BAD_REQUEST)
  }

  try {
    const shouldCommit = await initTransaction(req)

    const where = appendNonTrashedFilter({
      enableTrash: Boolean(collection.config.trash),
      trash: false,
      where: {
        _verificationToken: { equals: token },
      },
    })

    const user = await req.payload.db.findOne<any>({
      collection: collection.config.slug,

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Only call verifyEmail on local-strategy collections (`auth.disableLocalStrategy: false`).
  2. For external-strategy collections, handle email verification in the IdP.
  3. If you intend local email verification, ensure `auth.verify` is set and `disableLocalStrategy` is false.

Example fix

// before
await payload.verifyEmail({ collection, token, req })
// after
if (!collectionConfig.auth.disableLocalStrategy) {
  await payload.verifyEmail({ collection, token, req })
}
Defensive patterns

Strategy: validation

Validate before calling

// Skip verifyEmail for external-strategy collections
if (collection.config.auth.disableLocalStrategy) {
  throw new Error('Email verification is unavailable for this collection')
}
await payload.verifyEmail({ collection, token, req })

Type guard

function supportsEmailVerification(cfg: CollectionConfig): boolean {
  return !cfg.auth?.disableLocalStrategy && !!cfg.auth?.verify
}

Try / catch

if (collection.config.auth.disableLocalStrategy) {
  // verification handled by the IdP
} else {
  await payload.verifyEmail({ collection, token, req })
}

Prevention

When it happens

Trigger: 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.

Common situations: Verification links sent for an SSO collection; a shared email-verification pipeline that doesn't branch on strategy; misconfiguration mixing `verify` with `disableLocalStrategy`.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/651014ae0e1a01a3. Report an issue: GitHub.