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 by findVersionByIDOperation when the version lookup returns nothing but the caller is subject to a where-based access-control result (access returned a query object rather than a boolean). Payload interprets an empty result under a where-constraint as 'forbidden' rather than 'not found', to avoid leaking the existence of versions the user cannot read. Surfaced as HTTP 403 Forbidden.
Source
Thrown at packages/payload/src/globals/operations/findVersionByID.ts:100
where: combineQueries({ id: { equals: id } }, accessResults),
}
// /////////////////////////////////////
// Find by ID
// /////////////////////////////////////
if (!findGlobalVersionsArgs.where?.and?.[0]?.id) {
throw new NotFound(req.t)
}
const { docs: results } = await payload.db.findGlobalVersions(findGlobalVersionsArgs)
if (!results || results?.length === 0) {
if (!disableErrors) {
if (!hasWhereAccess) {
throw new NotFound(req.t)
}
if (hasWhereAccess) {
throw new Forbidden(req.t)
}
}
return null!
}
// Clone the result - it may have come back memoized
let result: any = deepCopyObjectSimple(results[0])
if (!result.version) {
result.version = {}
}
// Patch globalType onto version doc
result.version.globalType = globalConfig.slug
// /////////////////////////////////////
// beforeRead - CollectionView on GitHub (pinned to 00c58b35c0)
Solutions
- Confirm the authenticated user actually satisfies the readVersions constraint for that version.
- If callers legitimately need to probe existence, pass disableErrors:true and treat null as 'not visible'.
- Audit the readVersions access function — returning a Where object turns every miss into 403; ensure the query matches how versions store the gating field.
- For server-side privileged reads, set overrideAccess:true to bypass the where-constraint path.
Example fix
// before
const v = await payload.findGlobalVersionByID({ id, global: 'branding', overrideAccess: false })
// after
const v = await payload.findGlobalVersionByID({
id,
global: 'branding',
overrideAccess: false,
disableErrors: true,
})
if (v === null) {
// could be missing OR not permitted — handle as 'unavailable'
} Defensive patterns
Strategy: try-catch
Validate before calling
const allowed = await executeAccess(
{ id, slug, req },
globalConfig.access.readVersions,
)
// if typeof allowed === 'object', a miss becomes Forbidden — handle null Type guard
function isWhereAccess(
r: boolean | object,
): r is object {
return typeof r === 'object'
} Try / catch
try {
return await payload.findGlobalVersionByID({ id, global: slug, overrideAccess: false })
} catch (err) {
if (err instanceof Forbidden) return null // treat as 'not visible'
throw err
} Prevention
- Make readVersions return a query that matches the field actually stored on versions.
- Use disableErrors:true to fold missing+forbidden into a single null result.
When it happens
Trigger: Calling findVersionByID with overrideAccess:false where globalConfig.access.readVersions returned a Where query, and either the version does not exist or the user's constraint excludes it. GET /globals/:slug/versions/:id by a user whose readVersions access narrows the visible set.
Common situations: Role-based tenants where readVersions returns { tenant: { equals: user.tenant } } and the requested version belongs to another tenant; tightening access rules after versions were created; a logged-in user whose access function returns a query that no version satisfies.
Related errors
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- The global with slug ${String(globalSlug)} can't be found.
- The global with slug ${String(globalSlug)} can't be found.
- The global with slug ${String(globalSlug)} can't be found.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/e66a5af2579d3cb8.
Report an issue: GitHub.