mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Invalid BulkOperation, Batch cannot be empty

Error message

Invalid BulkOperation, Batch cannot be empty

What it means

Thrown by BulkOperationBase.execute() when no operations were ever added (this.s.batches is empty). Calling execute() on a bulk op where find()/insert()/raw() were never invoked is a programming error; the server would reject an empty batch anyway.

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 3366c21a63)

Solutions

  1. Guard execute() with a check: if (bulk.length === 0) return; before calling execute().
  2. Validate the input list is non-empty before initializing the bulk op.
  3. Use collection.bulkWrite(ops) and short-circuit when ops.length === 0.

Example fix

// before
const bulk = coll.initializeUnorderedBulkOp();
for (const op of ops) bulk.raw(op); // ops is []
await bulk.execute(); // throws

// after
if (ops.length === 0) return;
const bulk = coll.initializeUnorderedBulkOp();
for (const op of ops) bulk.raw(op);
await bulk.execute();
Defensive patterns

Strategy: validation

Validate before calling

function executeIfNonEmpty(bulk) {
  if (bulk.length === 0) return null;
  return bulk.execute();
}

Type guard

function bulkHasOps(bulk) {
  return bulk != null && typeof bulk.length === 'number' && bulk.length > 0;
}

Try / catch

try {
  await bulk.execute();
} catch (e) {
  if (e instanceof MongoInvalidArgumentError && /Batch cannot be empty/.test(e.message)) {
    return; // nothing to do
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling await bulk.execute() on a freshly initialized bulk op without adding any operations. Building operations conditionally such that none are added for an empty input list.

Common situations: Empty input arrays processed in a loop that adds zero ops; early-return logic that skips all op additions but still reaches execute(); tests that scaffold a bulk op but never populate it.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/5a6d951a644e7d7e.json. Report an issue: GitHub.