payloadcms/payload · error · APIError
Collection ${args.collection.config.slug} has disabled bulk
Error message
Collection ${args.collection.config.slug} has disabled bulk delete What it means
`deleteOperation` checks `disableBulkDelete` on the collection config; if true and the caller did not pass `overrideAccess`, it throws `APIError` (403). This is a deliberate safety guard preventing accidental mass deletion on collections flagged as bulk-delete-protected.
Source
Thrown at packages/payload/src/collections/operations/delete.ts:57
overrideAccess?: boolean
overrideLock?: boolean
populate?: PopulateType
req: PayloadRequest
showHiddenFields?: boolean
trash?: boolean
where: Where
} & Pick<FindOptions<string, SelectType>, 'select'>
export const deleteOperation = async <
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
>(
incomingArgs: Arguments,
): Promise<BulkOperationResult<TSlug, TSelect>> => {
let args = incomingArgs
if (args.collection.config.disableBulkDelete && !args.overrideAccess) {
throw new APIError(`Collection ${args.collection.config.slug} has disabled bulk delete`, 403)
}
try {
const shouldCommit = !args.disableTransaction && (await initTransaction(args.req))
// /////////////////////////////////////
// beforeOperation - Collection
// /////////////////////////////////////
args = await buildBeforeOperation({
args,
collection: args.collection.config,
operation: 'delete',
overrideAccess: args.overrideAccess!,
})
const {
collection: { config: collectionConfig },
depth,View on GitHub (pinned to 00c58b35c0)
Solutions
- Delete by ID (`payload.deleteByID`) instead of bulk delete, if appropriate.
- If this is legitimate privileged work, pass `overrideAccess: true`.
- Reconsider whether `disableBulkDelete` should remain true for this collection.
Example fix
// before
await payload.delete({ collection: 'orders', where: { status: { equals: 'archived' } } })
// after (privileged migration only)
await payload.delete({ collection: 'orders', where: { status: { equals: 'archived' } }, overrideAccess: true }) Defensive patterns
Strategy: validation
Validate before calling
function canBulkDelete(collectionConfig, overrideAccess = false) {
return !collectionConfig.disableBulkDelete || overrideAccess
} Type guard
function bulkDeleteAllowed(cfg, overrideAccess): boolean {
return overrideAccess || !cfg.disableBulkDelete
} Try / catch
try {
await payload.delete({ collection, where })
} catch (e) {
if (e?.statusCode === 403 && /disabled bulk delete/.test(e.message)) {
// fall back to per-ID deletes
} else throw e
} Prevention
- Check `disableBulkDelete` before issuing bulk deletes.
- Prefer `deleteByID` for guarded collections.
- Use `overrideAccess` only in trusted scripts.
When it happens
Trigger: Calling `payload.delete({ collection, where })` on a collection configured with `disableBulkDelete: true`, without `overrideAccess: true`.
Common situations: A cleanup/migration script targeting a protected collection; a UI bulk-action on a guarded collection; forgetting `overrideAccess` in privileged server code.
Related errors
- You are not allowed to perform this action.
- Collection ${collectionSlug} not found
- Invalid template given
- Invalid database type given. Valid types are: ${Object.value
- No package.json found in this project
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/e0ca945e8af5e6f2.
Report an issue: GitHub.