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
- Create a fresh bulk operation for each execution: `const bulk = coll.initializeUnorderedBulkOp(); ... await bulk.execute();` — do not reuse `bulk`.
- 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.
- 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
- Treat a bulk op as single-use: build, execute, discard.
- In retry logic, rebuild the bulk op from the source data on each attempt; do not retain the builder.
- Prefer collection.bulkWrite(ops) over the manual builder when retry is needed — it constructs a new bulk internally per call.
- Track an 'executed' flag in any wrapper that exposes execute().
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
- Cannot call abortTransaction after calling commitTransaction
- Cannot call abortTransaction twice
- Cannot call commitTransaction after calling abortTransaction
- Invalid BulkOperation, Batch cannot be empty
- Argument "operations" must be an array of documents
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)