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
- Serialize validations: await the first validate() before starting another, or cache the in-flight promise and reuse it
- Let save() drive validation instead of validating and saving in parallel
- Catch ParallelValidateError and retry after the running validation settles
- 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
- Never call validate() twice concurrently on the same document instance
- Share one in-flight validation promise across handlers that need the same doc
- Remember save() validates too - do not race validate() against save()
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
- Document.prototype.validate() no longer accepts a callback
- Cannot set both `validateAllPaths` and `pathsToSkip`
- Cannot set both `validateAllPaths` and `pathsToValidate`
- Cannot set both `validateAllPaths` and `validateModifiedOnly
- Cannot validate subdocument that does not have a parent
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/4ae45cca950d7abb.
Report an issue: GitHub.