payloadcms/payload · error · NotFound

Not Found

Error message

Not Found

What it means

Thrown by `getDuplicateDocumentData` when the source document for duplication is not found AND the collection's read access is not a where-policy. Payload first applies read access, fetches the latest version, and if nothing comes back with a plain boolean access result it responds HTTP 404. This mirrors updateByID's not-found semantics for the duplicate operation.

Source

Thrown at packages/payload/src/duplicateDocument/index.ts:73

  let duplicatedFromDocWithLocales = await getLatestCollectionVersion({
    id,
    config: collectionConfig,
    payload,
    query: findOneArgs,
    req,
  })

  if (selectedLocales && selectedLocales.length > 0 && duplicatedFromDocWithLocales) {
    duplicatedFromDocWithLocales = filterDataToSelectedLocales({
      configBlockReferences: payload.config.blocks,
      docWithLocales: duplicatedFromDocWithLocales,
      fields: collectionConfig.fields,
      selectedLocales,
    })
  }

  if (!duplicatedFromDocWithLocales && !hasWherePolicy) {
    throw new NotFound(req.t)
  }
  if (!duplicatedFromDocWithLocales && hasWherePolicy) {
    throw new Forbidden(req.t)
  }

  // remove the createdAt timestamp and rely on the db to set it
  if ('createdAt' in duplicatedFromDocWithLocales) {
    delete duplicatedFromDocWithLocales.createdAt
  }
  // remove the id and rely on the db to set it
  if ('id' in duplicatedFromDocWithLocales) {
    delete duplicatedFromDocWithLocales.id
  }

  duplicatedFromDocWithLocales = await beforeDuplicate({
    id,
    collection: collectionConfig,
    context: req.context,

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Verify the source id exists (`payload.findByID`) before duplicating.
  2. Surface a 'source not found' message to the user; do not retry.
  3. If trash is enabled, ensure the source is not soft-trashed or operate with `trash: true`.

Example fix

// before
await payload.duplicate({ collection: 'posts', id })
// after
const src = await payload.findByID({ collection: 'posts', id }).catch(() => null)
if (!src) throw new Error('source post not found')
await payload.duplicate({ collection: 'posts', id })
Defensive patterns

Strategy: validation

Validate before calling

const src = await payload.findByID({ collection, id, depth: 0, req }).catch(() => null)
if (!src) throw new Error(`Source ${id} not found; cannot duplicate`)
await payload.duplicate({ collection, id, req })

Try / catch

try {
  await payload.duplicate({ collection, id, req })
} catch (err) {
  if (err instanceof NotFound) return respond(404, 'Source document not found')
  throw err
}

Prevention

When it happens

Trigger: Calling `payload.duplicate({ id: '999' })` on a non-existent id; duplicating a hard-deleted document; the source id belongs to another collection.

Common situations: Stale 'duplicate' button in the UI pointing at a deleted doc; scripted duplication over a list where one id was removed; trashed source with `trash` not enabled in the lookup.

Related errors


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