Automattic/mongoose · error · MongooseBulkWriteError
${operation} failed with ${validationErrors.length} Mongoose
Error message
${operation} failed with ${validationErrors.length} Mongoose validation errors: ${preview} (operation is 'bulkWrite') What it means
After a bulkWrite whose ops partially failed schema casting, if validationErrors is non-empty and options.throwOnValidationError is true, Mongoose throws MongooseBulkWriteError with this message once the driver call finishes. Valid ops have already executed (any driver error is decorated and rethrown first), so this signals partial success: check .validationErrors for failed op indexes and the write result/res for what succeeded. Without the option, the result is returned decorated with mongoose.validationErrors.
Source
Thrown at lib/model.js:3564
if (error?.writeErrors) {
for (const writeError of error.writeErrors) {
writeErrorsByIndex[writeError.err.index] = writeError;
}
}
for (let i = 0; i < validOpIndexes.length; ++i) {
results[validOpIndexes[i]] = writeErrorsByIndex[i] ?? null;
}
if (error) {
if (validationErrors.length > 0) {
decorateBulkWriteResult(error, validationErrors, results);
}
await this.hooks.execPost('bulkWrite', this, [null], { error, filter: postFilter });
}
if (validationErrors.length > 0) {
if (options.throwOnValidationError) {
throw new MongooseBulkWriteError(
validationErrors,
results,
res,
'bulkWrite'
);
} else {
decorateBulkWriteResult(res, validationErrors, results);
}
}
}
await this.hooks.execPost('bulkWrite', this, [res], { filter: postFilter });
return res;
}
/**
* Takes an array of documents, gets the changes and inserts/updates documents in the databaseView on GitHub (pinned to 49cdab0136)
Solutions
- Catch MongooseBulkWriteError, iterate err.validationErrors (op index + message), and re-enqueue/fix only the failed operations
- Pre-validate each document with validateSync() before building ops so only valid ops are submitted
- Fix the source records or adjust the schema (remove required, widen enum) for rules that reject legitimate data
- Omit throwOnValidationError and read res.mongoose?.validationErrors from the returned result when partial completion is acceptable
Example fix
// before
await User.bulkWrite(ops, { throwOnValidationError: true });
// after
try {
await User.bulkWrite(ops, { throwOnValidationError: true });
} catch (err) {
if (err.name === 'MongooseBulkWriteError') {
const bad = err.validationErrors.map(v => v.index);
await retryQueue.push(...ops.filter((_, i) => bad.includes(i)));
} else throw err;
} Defensive patterns
Strategy: validation
Validate before calling
const validated = ops.map(op => {
if (op.insertOne) {
const err = new Model(op.insertOne.document).validateSync();
return { op, err };
}
return { op, err: null };
});
const good = validated.filter(x => !x.err).map(x => x.op);
const bad = validated.filter(x => x.err);
if (bad.length) report(bad);
await Model.bulkWrite(good); Try / catch
try {
const res = await Model.bulkWrite(ops, { throwOnValidationError: true });
} catch (err) {
if (err.name === 'MongooseBulkWriteError') {
const failedIndexes = err.validationErrors.map(v => v.index);
const failedOps = ops.filter((_, i) => failedIndexes.includes(i));
// valid ops already executed; requeue failedOps for repair
} else throw err;
} Prevention
- Treat MongooseBulkWriteError from bulkWrite as partial success: reconcile via validationErrors indexes
- Pre-validate insertOne/replaceOne documents with validateSync() before building ops
- Log op indexes with source record ids at build time so failures map back to inputs
When it happens
Trigger: await Model.bulkWrite(mixedOps, { throwOnValidationError: true }) where at least one insertOne/replaceOne/updateOne op carries a document that fails validation (required, enum, custom validator).
Common situations: Batched sync jobs over third-party data where a subset of records fails new validation rules; partial schema tightening (new required field) leaving older producers non-compliant.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- bulkWrite failed with ${validationErrors.length} Mongoose va
- Must provide a filter object.
- insertMany failed with ${validationErrors.length} Mongoose v
- Model.bulkWrite() no longer accepts a callback
- Arguments must be aggregate pipeline operators
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/ec954d8c0576f227.
Report an issue: GitHub.