payloadcms/payload · error · NotFound
Not Found
Error message
Not Found
What it means
Two `NotFound` throw sites in `findVersionByID`: (1) the combined `where` clause has no leading `id` filter (`where.and[0].id` missing), meaning the requested version id was dropped/malformed before the DB query; (2) the DB query returned no version rows and there is no where-access policy (with where-access it becomes `Forbidden`). Net effect: the requested global version does not exist or cannot be resolved for the caller.
Source
Thrown at packages/payload/src/globals/operations/findVersionByID.ts:90
}),
versions: true,
})
const findGlobalVersionsArgs: FindGlobalVersionsArgs = {
global: globalConfig.slug,
limit: 1,
locale: locale!,
req,
select,
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])View on GitHub (pinned to 00c58b35c0)
Solutions
- Confirm the version id exists via `findGlobalVersions` before requesting it.
- Validate the id is a well-formed string/number before the call so it survives into the where clause.
- Handle the 404 in the client and surface a 'version not found' state.
- If versions are draft-only, ensure drafts/versions are enabled on the global.
Example fix
// before (unknown id, throws NotFound)
await payload.findGlobalVersionByID({ slug: 'settings', id: someStaleId, req })
// after (verify then fetch)
const { docs } = await payload.findGlobalVersions({ slug: 'settings', where: { id: { equals: someStaleId } }, req })
if (!docs.length) return null
await payload.findGlobalVersionByID({ slug: 'settings', id: someStaleId, req }) Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: confirm the version exists for the caller before fetching by id
async function versionExists(slug, id, req) {
const { totalDocs } = await payload.countGlobalVersions({
slug, where: { id: { equals: id } }, req,
})
return totalDocs > 0
}
if (!await versionExists('settings', id, req)) return null Type guard
function isNotFoundOrForbidden(err: any): boolean {
return err?.name === 'NotFound' || err?.name === 'Forbidden' || err?.statusCode === 404 || err?.statusCode === 403
} Try / catch
try {
const v = await payload.findGlobalVersionByID({ slug: 'settings', id, req })
return v
} catch (err) {
if (err?.name === 'NotFound' || err?.statusCode === 404) {
// version missing or caller cannot resolve it — handle as 'not found'
return null
}
if (err?.name === 'Forbidden' || err?.statusCode === 403) {
// where-access excluded this version
return null
}
throw err
} Prevention
- Validate the version id shape before calling so it survives into the where clause.
- Use `findGlobalVersions` for user-facing version lists; fetch by id only from trusted references.
- Handle both 404 and 403 when versions are subject to where-access policies.
When it happens
Trigger: Calling `payload.findGlobalVersionByID` (or `GET /api/globals/<slug>/versions/<id>`) with an id that doesn't exist; passing a malformed/non-string id that gets stripped from the where clause; the version was deleted; the caller has where-access that excludes this version (then Forbidden).
Common situations: Stale version id stored in a client after the version was pruned; copy-pasting a URL with a truncated id; version truncation/limits deleting older versions; wrong global slug paired with a version id from another global.
Related errors
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/2fbafa07f68e8b60.
Report an issue: GitHub.