payloadcms/payload · error
Forbidden
Error message
Forbidden
What it means
A plain Error('Forbidden') thrown by the TanStack Start server adapter's forbidden() helper. TanStack Router has no dedicated forbidden primitive, so the adapter surfaces a generic error to terminate the request boundary when framework code calls adapter.forbidden() (e.g. access control denied during an admin page render).
Source
Thrown at packages/tanstack-start/src/utilities/serverAdapter.server.ts:93
},
redirect: (path: string) => {
// TanStack Router requires throwing the redirect() result directly
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw redirect({ to: path })
},
permanentRedirect: (path: string) => {
// TanStack Router does not have a separate permanent redirect primitive;
// fall back to a regular redirect so existing behavior is preserved.
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw redirect({ to: path })
},
forbidden: () => {
// TanStack Router does not have a dedicated forbidden() helper; surface
// a generic error so the request boundary still terminates the request.
throw new Error('Forbidden')
},
unauthorized: () => {
// TanStack Router does not have a dedicated unauthorized() helper; surface
// a generic error so the request boundary still terminates the request.
throw new Error('Unauthorized')
},
setCookie: (name: string, value: string, options?: CookieOptions) => {
setResponseHeader('Set-Cookie', serializeCookie(name, value, options))
},
}
/**
* Navigation requested during an admin page render, recorded by
* `createPageRenderServerAdapter`. The admin-page server function reads this
* after `renderServerComponent` resolves.
*/View on GitHub (pinned to 00c58b35c0)
Solutions
- Review the access-control logic that triggered forbidden() and confirm the user should indeed be denied.
- Ensure the user is authenticated and has the required role before reaching the guarded route.
- If using TanStack Start, handle this generic error in your error boundary to render a proper 403 page.
Example fix
// before — generic error, no status
forbidden: () => {
throw new Error('Forbidden')
},
// after — attach a status for the error boundary to map
import { Forbidden } from 'payload'
forbidden: () => {
throw new Forbidden()
}, Defensive patterns
Strategy: try-catch
Validate before calling
// The adapter's forbidden() is called by framework access checks;
// prevent it by ensuring access returns true for authorized users.
function userHasAccess(user: { role?: string } | null, requiredRole: string): boolean {
return Boolean(user && user.role === requiredRole)
}
if (!userHasAccess(req.user, 'admin')) {
// handle before the adapter calls forbidden()
} Type guard
function isAdapterForbidden(err: unknown): err is Error {
return err instanceof Error && err.message === 'Forbidden'
} Try / catch
try {
await renderAdminPage()
} catch (err) {
if (isAdapterForbidden(err)) {
renderForbiddenPage()
return
}
throw err
} Prevention
- Map this generic error in your error boundary to a proper 403 response.
- Confirm access-control logic before it reaches forbidden().
- Consider replacing the bare Error with Forbidden for type-narrowable handling.
When it happens
Trigger: Code in the admin page-render pipeline calls the adapter's forbidden() — typically because an access-control check returned false during a TanStack Start server render of an admin route.
Common situations: Access policy returns false for the current user on a TanStack Start admin route; the adapter is used before auth is fully resolved; a custom route guard calls forbidden() incorrectly.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unauthorized
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- You are not allowed to perform this action.
- You are not allowed to perform this action.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/7d2a496a0e949099.
Report an issue: GitHub.