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
- Verify the ID exists before deleting, or treat 404 as success for idempotent deletes.
- If trash collection is enabled and you mean to delete a trashed doc, pass `trash: true`.
- 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
- Make deletes idempotent by swallowing `NotFound`.
- Check existence only if you must distinguish missing docs from success.
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
- Not Found
- Migration aborted: version__status column not found in ${ver
- Migration aborted: version__status column not found in ${ver
- Collection ${args.collection.config.slug} has disabled bulk
- Missing 'where' query of documents to delete.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/83e1781930e0062f.
Report an issue: GitHub.