strapi/strapi · error
Cannot delete a draft document
Error message
Cannot delete a draft document
What it means
deleteDocument removes an entire document (all locales and the published+draft pair). It refuses to delete only the draft version, because that would orphan the published entry and break draftAndPublish integrity. When the content type has draftAndPublish enabled and params.status === 'draft', the call is rejected. To discard draft edits, use the discard-draft operation instead.
Source
Thrown at packages/core/core/src/services/document-service/repository.ts:389
const lookupQuery = await async.pipe(
validateParams,
omit('status'),
i18n.defaultLocale(contentType),
i18n.multiLocaleToLookup(contentType),
transformParamsToQuery(uid),
(query) => assoc('where', { ...query.where, documentId }, query)
)(params);
const selectionQuery = await async.pipe(
validateParams,
omit('status'),
pickSelectionParams,
transformParamsToQuery(uid)
)(params);
if (hasDraftAndPublish && params.status === 'draft') {
throw new Error('Cannot delete a draft document');
}
const entriesToDelete = await strapi.db.query(uid).findMany(lookupQuery);
const deletedEntries = await async.map(entriesToDelete, (entryToDelete: any) =>
entries.delete(entryToDelete.id, selectionQuery)
);
entriesToDelete.forEach(emitEvent('entry.delete'));
return { documentId, entries: deletedEntries };
}
async function create(opts = {} as any) {
const { documentId: _documentId, ...params } = opts;
const queryParams = await async.pipe(
validateParams,View on GitHub (pinned to 4a4101264d)
Solutions
- Delete the whole document: strapi.documents(uid).delete({ documentId }) (omit status).
- To remove only the published version, use status: 'published'.
- To discard draft changes, use the discard-draft action (strapi.documents(uid).discardDraft(...)) or unpublish as appropriate.
- Never pass status: 'draft' to deleteDocument on draftAndPublish content types.
Example fix
// before (throws)
await strapi.documents('api::article.article').delete({
documentId,
status: 'draft',
});
// after — delete the whole document
await strapi.documents('api::article.article').delete({ documentId });
// or discard only the draft changes
await strapi.documents('api::article.article').discardDraft({ documentId }); Defensive patterns
Strategy: validation
Validate before calling
function deleteDocumentSafe(uid, opts) {
const ct = strapi.contentType(uid);
const hasDP = ct.options?.draftAndPublish === true;
if (hasDP && opts.status === 'draft') {
throw new Error('Use discardDraft to remove draft changes, or omit status to delete the document.');
}
return strapi.documents(uid).delete(opts);
} Type guard
const isDraftDeleteAttempt = (ct, opts) => ct.options?.draftAndPublish === true && opts?.status === 'draft';
Prevention
- Map 'discard draft' UX to discardDraft, not delete.
- Omit status on delete to remove the whole document.
- Document the delete semantics for draftAndPublish types.
When it happens
Trigger: Calling strapi.documents('api::article.article').delete({ documentId, status: 'draft' }) on a content type with draftAndPublish enabled.
Common situations: Confusing delete with unpublish/discard-draft; a UI button wired to delete the draft; automation that loops over statuses to delete selectively.
Related errors
- Unable to count draft relations, document with id ${id} and
- Content-Type ${ctUID} doesn't exist
- Controller ${controllerUID} doesn't exist
- Hook ${uid} doesn't exist
- Middleware ${uid} has already been registered.
AI-assisted analysis of strapi/strapi@4a4101264d (2026-08-12).
Data as JSON: /api/errors/6aafa3b374187dad.
Report an issue: GitHub.