mongodb/node-mongodb-native · error · MongoBatchReExecutionError

This batch has already been executed, create new batch to…

Error message

This batch has already been executed, create new batch to execute

What it means

Thrown as MongoBatchReExecutionError by BulkOperationBase.execute() (src/bulk/common.ts:1183) when execute() is called on a bulk operation that has already run. BulkOperationBase mutates internal state (s.executed = true) and consumes its batches during execution, so a second execution cannot rebuild the same batches; the driver forces you to create a new bulk operation instead.

Solutions

  1. Create a fresh bulk operation for each execution: `const bulk = coll.initializeUnorderedBulkOp(); ... await bulk.execute();` — do not reuse `bulk`.
  2. If you need to retry the same logical writes, rebuild the bulk op from the source data each attempt, or use collection.bulkWrite(...) which constructs a new bulk internally per call.
  3. Audit retry logic to ensure it does not retain the bulk builder across attempts.

Example fix

// before
const bulk = coll.initializeOrderedBulkOp();
bulk.find({ _id: 1 }).updateOne({ $set: { a: 1 } });
await bulk.execute();
await bulk.execute(); // throws MongoBatchReExecutionError

// after
async function runOnce() {
  const bulk = coll.initializeOrderedBulkOp();
  bulk.find({ _id: 1 }).updateOne({ $set: { a: 1 } });
  return bulk.execute();
}
await runOnce();
await runOnce();
Defensive patterns

Strategy: validation

Validate before calling

let bulkExecuted = false;
async function executeOnce(bulk: OrderedBulkOperation | UnorderedBulkOperation) {
  if (bulkExecuted) throw new Error('bulk already executed; build a new one');
  bulkExecuted = true;
  return bulk.execute();
}

// better: always construct a fresh bulk op per execution
async function runBulk(ops: (() => void)[]) {
  const bulk = coll.initializeUnorderedBulkOp();
  ops.forEach(fn => fn()); // rebuild
  return bulk.execute();
}

Type guard

// There is no exposed 'isExecuted' on the public type; track lifecycle externally.
// Wrap the bulk op in a guard object:
function onceBulk<T extends { execute: (...a: any[]) => Promise<any> }>(bulk: T) {
  let done = false;
  return {
    bulk,
    async execute(...args: Parameters<T['execute']>) {
      if (done) throw new Error('bulk already executed; create a new one');
      done = true;
      return bulk.execute(...args);
    }
  };
}

Try / catch

try {
  await bulk.execute();
} catch (e) {
  if (e instanceof MongoBatchReExecutionError) {
    // rebuild a new bulk op from source data instead of reusing
    const fresh = coll.initializeOrderedBulkOp();
    // ... re-add operations ...
    await fresh.execute();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `await bulk.execute()` twice on the same OrderedBulkOperation/UnorderedBulkOperation instance. The guard is `if (this.s.executed)` at src/bulk/common.ts:1182.

Common situations: Retrying an operation in a try/catch by re-running the same builder, calling execute() in a loop that reuses the bulk object, or a refactor that wraps execute() without recreating the bulk op.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/4cfe6cd825475abf. Report an issue: GitHub.

Appendix: source

Thrown at src/bulk/common.ts:1183

  get writeConcern(): WriteConcern | undefined {
    return this.s.writeConcern;
  }

  get batches(): Batch[] {
    const batches = [...this.s.batches];
    if (this.isOrdered) {
      if (this.s.currentBatch) batches.push(this.s.currentBatch);
    } else {
      if (this.s.currentInsertBatch) batches.push(this.s.currentInsertBatch);
      if (this.s.currentUpdateBatch) batches.push(this.s.currentUpdateBatch);
      if (this.s.currentRemoveBatch) batches.push(this.s.currentRemoveBatch);
    }
    return batches;
  }

  async execute(options: BulkWriteOptions = {}): Promise<BulkWriteResult> {
    if (this.s.executed) {
      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');

View on GitHub (pinned to dce7939f86)