Budibase/budibase · error · CouchDBError
Unable to bulk remove documents: ${res.error}
Error message
Unable to bulk remove documents: ${res.error} What it means
bulkRemove in DatabaseImpl aggregates per-document errors from a CouchDB bulk docs response and, if any document failed to delete, throws a CouchDBError with status 400 containing every accumulated error message. It means at least one document in the batch could not be removed, while others may have been deleted successfully (partial failure is possible).
Source
Thrown at packages/backend-core/src/db/couch/DatabaseImpl.ts:310
docs: documents.map(doc => ({
...doc,
_deleted: true,
})),
})
})
if (opts?.silenceErrors) {
return
}
let errorFound = false
let errorMessage = "Unable to bulk remove documents: "
for (let res of response) {
if (res.error) {
errorFound = true
errorMessage += res.error
}
}
if (errorFound) {
throw new CouchDBError(errorMessage, {
name: this.name,
status: 400,
})
}
}
async post(document: AnyDocument, opts?: DatabasePutOpts) {
if (!document._id) {
document._id = newid()
}
return this.put(document, opts)
}
async put(document: AnyDocument, opts?: DatabasePutOpts) {
if (!document._id) {
throw new Error("Cannot store document without _id field.")
}
return this.performCallWithDBCreation(async db => {View on GitHub (pinned to a81a902e9a)
Solutions
- Re-fetch each document immediately before bulkRemove so _id and _rev are current
- Parse res.error rows from the CouchDB bulk response and retry only the failed docs individually with force/updated revs
- Check that no protected/design documents (starting with _design/) are included in the batch
- Verify the user/role has write access to the database
Example fix
// before
await db.bulkRemove(staleDocs)
// after
const fresh = await db.bulkGet({ docs: staleDocs.map(d => ({ id: d._id })) })
const deletable = fresh.results.map(r => r.docs[0].ok).filter(Boolean).map(d => ({ ...d, _deleted: true }))
await db.bulkRemove(deletable) Defensive patterns
Strategy: try-catch
Validate before calling
const invalid = docs.filter(d => !d._id || !d._rev)
if (invalid.length) throw new Error(`Docs missing _id/_rev: ${invalid.map(d => d._id).join(",")}`) Type guard
function isDeletable(d: any): d is { _id: string; _rev: string } {
return typeof d._id === "string" && typeof d._rev === "string"
} Try / catch
try {
await db.bulkRemove(docs)
} catch (e) {
if (e.name === "CouchDBError" && e.status === 400) {
// re-fetch failed docs individually and delete with fresh revs
}
} Prevention
- Always bulk-remove freshly fetched documents
- Filter out _design/ docs before deleting
- Handle partial failure: re-read docs and retry only failures
When it happens
Trigger: Calling db.bulkRemove(docs) where one or more docs have a bad/missing _id, a wrong _rev (conflict), or the doc does not exist; CouchDB returns an error object for those rows.
Common situations: Deleting stale UI state that was concurrently modified or deleted elsewhere; passing docs fetched long ago with outdated _rev; bulk-deleting ids that never existed; permission/restricted design docs in the batch.
Related errors
- Project revision does not match.
- DB does not exist
- ${err.message}
- CouchDB error: ${err.message}
- Unable to get bulk documents: ${missingIds}
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/b72bbf41ab24fd6e.
Report an issue: GitHub.