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 aggregateOptions
What it means
Thrown by AggregationCursor._initialize when the aggregateOptions simultaneously enable `explain`, `timeoutMS`, and either `maxTimeMS` or `explain.maxTimeMS`. The driver wraps the underlying validateExplainTimeoutOptions failure because combining a server-side maxTimeMS deadline with the client-side timeoutMS deadline on an explain command is ambiguous and disallowed by the client-side operation timeout spec. Pick one timeout mechanism for explain queries.
Solutions
- Remove maxTimeMS from aggregateOptions and rely on timeoutMS alone for the explain.
- Or remove timeoutMS and keep maxTimeMS for the explain.
- Prefer cursor.explain(verbosity, { timeoutMS }) which resolves explain/timeout options correctly via resolveExplainTimeoutOptions.
- If explain.maxTimeMS is nested inside the explain object, drop that nested maxTimeMS field.
Example fix
// before
await collection.aggregate(pipeline, {
explain: { verbosity: 'queryPlanner', maxTimeMS: 50 },
timeoutMS: 1000
}).toArray();
// after
await collection.aggregate(pipeline).explain('queryPlanner', { timeoutMS: 1000 }); Defensive patterns
Strategy: validation
Validate before calling
function assertExplainTimeoutOptions(opts) {
if (opts.explain == null) return;
const hasTimeoutMS = opts.timeoutMS != null;
const hasMaxTimeMS = opts.maxTimeMS != null ||
(typeof opts.explain === 'object' && opts.explain.maxTimeMS != null);
if (hasTimeoutMS && hasMaxTimeMS) {
throw new Error('Cannot combine timeoutMS with explain+maxTimeMS on aggregate; remove one.');
}
} Try / catch
try {
await cursor.toArray();
} catch (e) {
if (e instanceof MongoAPIError && /timeoutMS cannot be used with explain/.test(e.message)) {
// strip maxTimeMS and retry via cursor.explain()
} else throw e;
} Prevention
- Always use cursor.explain(verbosity, { timeoutMS }) instead of mixing explain+timeoutMS in the constructor options.
- Audit option builders for accidental maxTimeMS+timeoutMS co-occurrence.
When it happens
Trigger: Calling collection.aggregate(pipeline, { explain: { verbosity: 'queryPlanner', maxTimeMS: 50 }, timeoutMS: 1000 }) and then iterating; or passing { explain: true, maxTimeMS: 50, timeoutMS: 1000 } in aggregateOptions; or setting cursorOptions.maxTimeMS and timeoutMS together when explain is truthy in the aggregate options.
Common situations: Migrating from maxTimeMS to timeoutMS without removing the old value; copy-pasting options from a non-explain query into an explain run; wrapper libraries that default both timeouts on; upgrading the driver to a version that enforces client-side operation timeout rules strictly.
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…
- Cannot use maxTimeMS with timeoutMS for explain commands.
- An operation cannot be given a timeoutMS setting when…
- Argument for maxAwaitTimeMS must be a number
- Argument for maxTimeMS must be a number
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/c78c41f26535b713.
Report an issue: GitHub.
Appendix: source
Thrown at src/cursor/aggregation_cursor.ts:84
}
override map<T>(transform: (doc: TSchema) => T): AggregationCursor<T> {
return super.map(transform) as AggregationCursor<T>;
}
/** @internal */
async _initialize(session: ClientSession): Promise<InitialCursorResponse> {
const options = {
...this.aggregateOptions,
...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 aggregateOptions'
);
}
}
const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, options);
const response = await executeOperation(this.client, aggregateOperation, this.timeoutContext);
return { server: aggregateOperation.server, session, response };
}
/** Execute the explain for the cursor */
async explain(): Promise<Document>;
async explain(verbosity: ExplainVerbosityLike | ExplainCommandOptions): Promise<Document>;
async explain(options: { timeoutMS?: number }): Promise<Document>;
async explain(
verbosity: ExplainVerbosityLike | ExplainCommandOptions,View on GitHub (pinned to dce7939f86)