payloadcms/payload · error · APIError

Missing ID of version to restore.

Error message

Missing ID of version to restore.

What it means

Thrown at packages/payload/src/collections/operations/restoreVersion.ts:75 with HTTP 400 (BAD_REQUEST) inside `restoreVersionOperation` when the `id` argument is falsy (undefined, null, empty string, 0). It fires after `beforeOperation` hooks but before any database lookup, so an empty id never reaches the versions query.

Source

Thrown at packages/payload/src/collections/operations/restoreVersion.ts:75

    showHiddenFields,
  } = args

  try {
    const shouldCommit = !args.disableTransaction && (await initTransaction(args.req))

    // /////////////////////////////////////
    // beforeOperation - Collection
    // /////////////////////////////////////

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

    if (!id) {
      throw new APIError('Missing ID of version to restore.', httpStatus.BAD_REQUEST)
    }

    // /////////////////////////////////////
    // Retrieve original raw version
    // /////////////////////////////////////

    const { docs: versionDocs } = await req.payload.db.findVersions({
      collection: collectionConfig.slug,
      limit: 1,
      locale: 'all',
      pagination: false,
      req,
      where: { id: { equals: id } },
    })

    const [rawVersionToRestore] = versionDocs

    if (!rawVersionToRestore) {

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Ensure a non-empty version id is passed to `payload.restoreVersion`.
  2. Validate the incoming id at the API boundary (typeof string/number, non-empty) before calling the Local API.
  3. If the id is optional in your flow, branch before calling restoreVersion rather than passing undefined.

Example fix

// before
await payload.restoreVersion({ collection: 'pages', id: req.body.versionId }) // versionId undefined

// after
const versionId = req.body.versionId
if (!versionId) {
  return res.status(400).json({ error: 'versionId is required' })
}
await payload.restoreVersion({ collection: 'pages', id: versionId })
Defensive patterns

Strategy: validation

Validate before calling

function assertVersionId(id: unknown): asserts id is string | number {
  if (id === undefined || id === null || id === '' || id === 0) {
    throw new Error('A non-empty version id is required for restoreVersion.')
  }
}

assertVersionId(versionId)
await payload.restoreVersion({ collection: 'pages', id: versionId })

Type guard

const hasVersionId = (id: unknown): id is string | number =>
  (typeof id === 'string' && id.length > 0) || (typeof id === 'number' && id > 0)

if (hasVersionId(versionId)) {
  await payload.restoreVersion({ collection: 'pages', id: versionId })
} else {
  // reject the request at the boundary
}

Try / catch

try {
  await payload.restoreVersion({ collection: 'pages', id: versionId })
} catch (err) {
  if (err instanceof APIError && err.status === 400 && /Missing ID of version/.test(err.message)) {
    // caller omitted the id — return a 400 to the client
  } else throw err
}

Prevention

When it happens

Trigger: Calling `payload.restoreVersion({ collection, id: undefined })` or `id: ''`; reading the id from a URL param that is missing and passing it through without validation; destructuring `id` from a request body where the field is absent.

Common situations: Route handler does not validate the `:id` path parameter; a UI 'Restore' button submits without a selected version; refactoring that renames the field and leaves `id` undefined.

Related errors


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