payloadcms/payload · error · Forbidden

error:notAllowedToPerformAction

Error message

error:notAllowedToPerformAction

What it means

Thrown in `unlock` when `collectionConfig.auth.disableLocalStrategy` is true. With no local credential store there is nothing to lock/unlock, so the operation refuses. `Forbidden` (HTTP 403).

Source

Thrown at packages/payload/src/auth/operations/unlock.ts:52

    overrideAccess,
    req: { locale },
    req,
  } = args

  const loginWithUsername = collectionConfig.auth.loginWithUsername

  const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)

  const sanitizedEmail = canLoginWithEmail && (args.data?.email || '').toLowerCase().trim()
  const sanitizedUsername =
    (canLoginWithUsername &&
      'username' in args.data &&
      typeof args.data.username === 'string' &&
      args.data.username.toLowerCase().trim()) ||
    null

  if (collectionConfig.auth.disableLocalStrategy) {
    throw new Forbidden(req.t)
  }
  if (!sanitizedEmail && !sanitizedUsername) {
    throw new APIError(
      `Missing ${collectionConfig.auth.loginWithUsername ? 'username' : 'email'}.`,
      httpStatus.BAD_REQUEST,
    )
  }

  try {
    args = await buildBeforeOperation({
      args,
      collection: args.collection.config,
      operation: 'unlock',
      overrideAccess,
    })

    const shouldCommit = await initTransaction(req)
    let whereConstraint: Where = {}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Only call unlock on collections with a local strategy (`disableLocalStrategy: false`).
  2. For external-strategy collections, manage lock/unlock through the IdP.
  3. Branch your code on `collectionConfig.auth.disableLocalStrategy` before invoking unlock.

Example fix

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

Strategy: validation

Validate before calling

// Skip unlock for external-strategy collections
if (collectionConfig.auth.disableLocalStrategy) {
  throw new Error('Unlock is unavailable for this collection')
}
await payload.unlock({ collection, data, req })

Type guard

function supportsUnlock(cfg: CollectionConfig): boolean {
  return !cfg.auth?.disableLocalStrategy
}

Try / catch

if (collectionConfig.auth.disableLocalStrategy) {
  // manage lock state via the IdP
} else {
  await payload.unlock({ collection, data, req })
}

Prevention

When it happens

Trigger: A collection configured with `auth: { disableLocalStrategy: true }` receives an unlock request (`POST /api/<collection>/unlock` or Local API `payload.unlock`). The guard fires before any lookup.

Common situations: Shared unlock UI/flow that doesn't branch on strategy; misconfigured collection that should support local lockout; calling the wrong collection slug for unlock.

Related errors


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