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 database

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Catch MongooseBulkWriteError, iterate err.validationErrors (op index + message), and re-enqueue/fix only the failed operations
  2. Pre-validate each document with validateSync() before building ops so only valid ops are submitted
  3. Fix the source records or adjust the schema (remove required, widen enum) for rules that reject legitimate data
  4. 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

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


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/ec954d8c0576f227. Report an issue: GitHub.