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

  1. Confirm the version id exists via `findGlobalVersions` before requesting it.
  2. Validate the id is a well-formed string/number before the call so it survives into the where clause.
  3. Handle the 404 in the client and surface a 'version not found' state.
  4. 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

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.