mongodb/node-mongodb-native · error · MongoAPIError
timeoutMS cannot be used with explain when explain is…
Error message
timeoutMS cannot be used with explain when explain is specified in findOptions
What it means
Thrown by FindCursor._initialize when findOptions enable explain together with timeoutMS and either maxTimeMS or explain.maxTimeMS. Same rule as aggregation: an explain command cannot carry both a server-side maxTimeMS deadline and the client-side timeoutMS deadline. The inner validateExplainTimeoutOptions error is caught and rethrown with this find-specific message.
Solutions
- Remove maxTimeMS (or explain.maxTimeMS) from findOptions when timeoutMS is set.
- Or remove timeoutMS and keep maxTimeMS for the explain run.
- Use cursor.explain(verbosity, { timeoutMS }) which resolves the combination correctly.
Example fix
// before
await collection.find(
{},
{ explain: { verbosity: 'queryPlanner', maxTimeMS: 50 }, timeoutMS: 1000 }
).toArray();
// after
await collection.find({}).explain('queryPlanner', { timeoutMS: 1000 }); Defensive patterns
Strategy: validation
Validate before calling
function assertFindExplainTimeout(opts) {
if (opts.explain == null) return;
const conflict = opts.timeoutMS != null &&
(opts.maxTimeMS != null ||
(typeof opts.explain === 'object' && opts.explain.maxTimeMS != null));
if (conflict) throw new Error('Remove maxTimeMS or timeoutMS before running explain on find.');
} Try / catch
try { await cursor.explain('queryPlanner'); }
catch (e) { if (e instanceof MongoAPIError && /timeoutMS cannot be used with explain/.test(e.message)) { /* drop maxTimeMS, retry */ } else throw e; } Prevention
- Run explain via cursor.explain(verbosity, { timeoutMS }) rather than embedding explain in find options.
- Never set both timeoutMS and maxTimeMS on the same find options object.
When it happens
Trigger: collection.find({}, { explain: { verbosity: 'queryPlanner', maxTimeMS: 50 }, timeoutMS: 1000 }) then iterating; or cursor.addQueryModifier('$maxTimeMS', 50) plus findOptions.timeoutMS plus explain; or constructing FindCursor directly with explain+maxTimeMS+timeoutMS.
Common situations: Migrating query tuning scripts that previously used maxTimeMS to the newer timeoutMS; reusing an options builder across explain and non-explain queries; upgrading the driver to a timeoutMS-enforcing version.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeoutMS cannot be used with explain when explain is…
- Argument for maxAwaitTimeMS must be a number
- Argument for maxTimeMS must be a number
- Cannot use maxTimeMS with timeoutMS for explain commands.
- Invalid first parameter to count
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/045d6db349ec898e.
Report an issue: GitHub.
Appendix: source
Thrown at src/cursor/find_cursor.ts:84
override map<T>(transform: (doc: TSchema) => T): FindCursor<T> {
return super.map(transform) as FindCursor<T>;
}
/** @internal */
async _initialize(session: ClientSession): Promise<InitialCursorResponse> {
const options = {
...this.findOptions, // NOTE: order matters here, we may need to refine this
...this.cursorOptions,
session,
signal: this.signal
};
if (options.explain) {
try {
validateExplainTimeoutOptions(options, Explain.fromOptions(options));
} catch {
throw new MongoAPIError(
'timeoutMS cannot be used with explain when explain is specified in findOptions'
);
}
}
const findOperation = new FindOperation(this.namespace, this.cursorFilter, options);
const response = await executeOperation(this.client, findOperation, this.timeoutContext);
// the response is not a cursor when `explain` is enabled
this.numReturned = response.batchSize;
return { server: findOperation.server, session, response };
}
/** @internal */
override async getMore(): Promise<CursorResponse> {
const numReturned = this.numReturned;View on GitHub (pinned to dce7939f86)