payloadcms/payload · warning · NotFound

Not Found

Error message

Not Found

What it means

In `deleteByIDOperation`, after access and query, if `docToDelete` is null and there is no where-access constraint, it throws `NotFound`. The document ID does not exist, was already deleted, or is filtered out by the trash filter.

Source

Thrown at packages/payload/src/collections/operations/deleteByID.ts:129

    let where = combineQueries({ id: { equals: id } }, accessResults)

    // Exclude trashed documents when trash: false
    where = appendNonTrashedFilter({
      enableTrash: collectionConfig.trash,
      trash,
      where,
    })

    const docToDelete = await req.payload.db.findOne({
      collection: collectionConfig.slug,
      locale: req.locale!,
      req,
      where,
    })

    if (!docToDelete && !hasWhereAccess) {
      throw new NotFound(req.t)
    }
    if (!docToDelete && hasWhereAccess) {
      throw new Forbidden(req.t)
    }

    // /////////////////////////////////////
    // Handle potentially locked documents
    // /////////////////////////////////////

    await checkDocumentLockStatus({
      id,
      collectionSlug: collectionConfig.slug,
      lockErrorMessage: `Document with ID ${id} is currently locked and cannot be deleted.`,
      overrideLock,
      req,
    })

    await deleteAssociatedFiles({

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Verify the ID exists before deleting, or treat 404 as success for idempotent deletes.
  2. If trash collection is enabled and you mean to delete a trashed doc, pass `trash: true`.
  3. Make delete handlers idempotent by swallowing `NotFound`.

Example fix

// before
await payload.deleteByID({ collection: 'posts', id })
// after
try {
  await payload.deleteByID({ collection: 'posts', id })
} catch (e) {
  if (e?.name !== 'NotFound') throw e
  // already gone — idempotent success
}
Defensive patterns

Strategy: try-catch

Validate before calling

async function docExists(payload, slug, id) {
  const doc = await payload.findByID({ collection: slug, id, disableErrors: true, overrideAccess: true })
  return !!doc
}

Type guard

function isNotFound(e): boolean {
  return e?.statusCode === 404 || e?.name === 'NotFound'
}

Try / catch

try {
  await payload.deleteByID({ collection, id })
} catch (e) {
  if (isNotFound(e)) return { ok: true, alreadyDeleted: true }
  throw e
}

Prevention

When it happens

Trigger: `payload.deleteByID({ collection, id })` (or `DELETE /api/{collection}/{id}`) with an ID that doesn't exist, was already deleted, or — when trash collection is enabled — is currently trashed while `trash` defaults to false.

Common situations: Stale client referencing a deleted doc; a race where another process deleted first; soft-delete/trash enabled so the trashed doc is excluded from the default lookup.

Related errors


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