mongodb/node-mongodb-native · error · MongoInvalidArgumentError
Invalid BulkOperation, Batch cannot be empty
Error message
Invalid BulkOperation, Batch cannot be empty
What it means
Thrown as MongoInvalidArgumentError by BulkOperationBase.execute() (src/bulk/common.ts:1201) when execute() is called on a bulk operation to which no operations were added. The driver has no batch to send, so it refuses to issue an empty wire command rather than round-tripping a no-op.
Solutions
- Guard the execute() call: only call it when you have at least one operation (`if (myOps.length) await coll.bulkWrite(myOps)`).
- Skip the whole bulk block when the input list is empty.
- If using the builder API, track whether you added any ops before invoking execute().
Example fix
// before
const bulk = coll.initializeUnorderedBulkOp();
for (const id of []) { // empty input
bulk.find({ _id: id }).deleteOne();
}
await bulk.execute(); // throws: Batch cannot be empty
// after
const ids: unknown[] = [];
if (ids.length > 0) {
const bulk = coll.initializeUnorderedBulkOp();
for (const id of ids) bulk.find({ _id: id }).deleteOne();
await bulk.execute();
} Defensive patterns
Strategy: validation
Validate before calling
if (myOps.length === 0) {
// nothing to do — skip execute entirely
return;
}
const bulk = coll.initializeUnorderedBulkOp();
for (const op of myOps) /* add ops */;
await bulk.execute(); Type guard
function hasAny<T>(arr: readonly T[]): arr is [T, ...T[]] {
return arr.length > 0;
}
if (hasAny(myOps)) {
// build and execute the bulk
} Try / catch
try {
await bulk.execute();
} catch (e) {
if (e instanceof MongoInvalidArgumentError && /Batch cannot be empty/.test(e.message)) {
// no-op: nothing to write; swallow or log
} else throw e;
} Prevention
- Guard execute() behind a non-empty check on the operations you intend to send.
- Skip the entire bulk block when the input list is empty.
- Track an 'added any ops?' boolean when using the builder API.
- Prefer collection.bulkWrite(ops) guarded by `if (ops.length)`.
When it happens
Trigger: Calling `await coll.initializeOrderedBulkOp().execute()` (or the unordered variant) without any preceding insert/find/update/delete calls. Guarded by `if (this.s.batches.length === 0)` at src/bulk/common.ts:1200.
Common situations: A loop that builds ops conditionally produced zero ops, a code path that skips all operations due to empty input, or calling execute() defensively at the end of a function without checking that ops were added.
Related errors
- Argument "operations" must be an array of documents
- Bulk find operation must specify a selector
- bulkWrite only supports insertOne, updateOne, updateMany…
- Document is larger than the maximum size
- Document is larger than the maximum size
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/5a6d951a644e7d7e.
Report an issue: GitHub.
Appendix: source
Thrown at src/bulk/common.ts:1201
throw new MongoBatchReExecutionError();
}
const writeConcern = WriteConcern.fromOptions(options);
if (writeConcern) {
this.s.writeConcern = writeConcern;
}
// If we have current batch
if (this.isOrdered) {
if (this.s.currentBatch) this.s.batches.push(this.s.currentBatch);
} else {
if (this.s.currentInsertBatch) this.s.batches.push(this.s.currentInsertBatch);
if (this.s.currentUpdateBatch) this.s.batches.push(this.s.currentUpdateBatch);
if (this.s.currentRemoveBatch) this.s.batches.push(this.s.currentRemoveBatch);
}
// If we have no operations in the bulk raise an error
if (this.s.batches.length === 0) {
throw new MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty');
}
this.s.executed = true;
const finalOptions = resolveOptions(this.collection, { ...this.s.options, ...options });
// if there is no timeoutContext provided, create a timeoutContext and use it for
// all batches in the bulk operation
finalOptions.timeoutContext ??= TimeoutContext.create({
session: finalOptions.session,
timeoutMS: finalOptions.timeoutMS,
serverSelectionTimeoutMS: this.collection.client.s.options.serverSelectionTimeoutMS,
waitQueueTimeoutMS: this.collection.client.s.options.waitQueueTimeoutMS
});
if (finalOptions.session == null) {
// if there is not an explicit session provided to `execute()`, create
// an implicit session and use that for all batches in the bulk operation
return await this.collection.client.withSession({ explicit: false }, async session => {View on GitHub (pinned to dce7939f86)