Automattic/mongoose · error · ParallelValidateError

Can't validate() the same doc multiple times in parallel. Do

Error message

Can't validate() the same doc multiple times in parallel. Document: ${doc._doc._id}

What it means

ParallelValidateError: validate() sets an internal `$__.validating` flag for the duration of async validation; a second validate() on the same document before the first finishes throws this error (message includes the document's _id). Subdocuments skip the check entirely.

Source

Thrown at lib/document.js:2760

  }
  this.$op = 'validate';

  if (arguments.length === 1) {
    if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) {
      options = arguments[0];
      pathsToValidate = null;
    }
  }
  if (options && typeof options.pathsToSkip === 'string') {
    const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1;
    options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' ');
  }
  const _skipParallelValidateCheck = options?._skipParallelValidateCheck;

  if (this.$isSubdocument != null) {
    // Skip parallel validate check for subdocuments
  } else if (this.$__.validating && !_skipParallelValidateCheck) {
    throw new ParallelValidateError(this);
  } else if (!_skipParallelValidateCheck) {
    this.$__.validating = true;
  }

  const hasValidateHooks = this.$__middleware.hasHooks('validate');
  try {
    try {
      if (hasValidateHooks) {
        [options] = await this._execDocumentPreHooks('validate', options, [options]);
      } else if (!_skipParallelValidateCheck) {
        // Even with no validate hooks, preserve the async boundary that the pre
        // hook `await` used to provide so that the parallel validate check still
        // observes `$__.validating` across a tick (gh-8468). insertMany's per-doc
        // validate passes `_skipParallelValidateCheck` and stays fully synchronous.
        await Promise.resolve();
      }
    } catch (error) {
      if (hasValidateHooks) {

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Serialize validations: await the first validate() before starting another, or cache the in-flight promise and reuse it
  2. Let save() drive validation instead of validating and saving in parallel
  3. Catch ParallelValidateError and retry after the running validation settles
  4. As a last resort pass `{ _skipParallelValidateCheck: true }` (internal option, use with care)

Example fix

// before
await Promise.all([doc.validate(), doc.validate()]); // second call throws ParallelValidateError

// after (share one in-flight validation)
doc.__validate = doc.__validate ?? doc.validate();
await doc.__validate;
Defensive patterns

Strategy: retry

Validate before calling

// Serialize validations on a shared document instance
doc.__validate = doc.__validate ?? doc.validate().finally(() => { delete doc.__validate; });
await doc.__validate;

Try / catch

try {
  await doc.validate();
} catch (err) {
  if (err instanceof mongoose.Error.ParallelValidateError) {
    await new Promise(resolve => setImmediate(resolve)); // let the in-flight validation finish
    return doc.validate();
  }
  throw err;
}

Prevention

When it happens

Trigger: `Promise.all([doc.validate(), doc.validate()])`; calling doc.validate() while doc.save() (which itself validates) is still in flight; two concurrent request handlers validating the same document instance.

Common situations: Document instances shared across parallel requests; hooks that validate while the caller also validates; retry loops that overlap an in-flight validation; fire-and-forget validate calls.

Related errors


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