{"record":{"id":"c78c41f26535b713","repo":"mongodb/node-mongodb-native","slug":"timeoutms-cannot-be-used-with-explain-when-explain","errorCode":null,"errorMessage":"timeoutMS cannot be used with explain when explain is specified in aggregateOptions","messagePattern":"timeoutMS cannot be used with explain when explain is specified in aggregateOptions","errorType":"exception","errorClass":"MongoAPIError","httpStatus":null,"severity":"error","filePath":"src/cursor/aggregation_cursor.ts","lineNumber":84,"sourceCode":"  }\n\n  override map<T>(transform: (doc: TSchema) => T): AggregationCursor<T> {\n    return super.map(transform) as AggregationCursor<T>;\n  }\n\n  /** @internal */\n  async _initialize(session: ClientSession): Promise<InitialCursorResponse> {\n    const options = {\n      ...this.aggregateOptions,\n      ...this.cursorOptions,\n      session,\n      signal: this.signal\n    };\n    if (options.explain) {\n      try {\n        validateExplainTimeoutOptions(options, Explain.fromOptions(options));\n      } catch {\n        throw new MongoAPIError(\n          'timeoutMS cannot be used with explain when explain is specified in aggregateOptions'\n        );\n      }\n    }\n\n    const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, options);\n\n    const response = await executeOperation(this.client, aggregateOperation, this.timeoutContext);\n\n    return { server: aggregateOperation.server, session, response };\n  }\n\n  /** Execute the explain for the cursor */\n  async explain(): Promise<Document>;\n  async explain(verbosity: ExplainVerbosityLike | ExplainCommandOptions): Promise<Document>;\n  async explain(options: { timeoutMS?: number }): Promise<Document>;\n  async explain(\n    verbosity: ExplainVerbosityLike | ExplainCommandOptions,","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/cursor/aggregation_cursor.ts#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait collection.aggregate(pipeline, {\n  explain: { verbosity: 'queryPlanner', maxTimeMS: 50 },\n  timeoutMS: 1000\n}).toArray();\n\n// after\nawait collection.aggregate(pipeline).explain('queryPlanner', { timeoutMS: 1000 });","handlingStrategy":"validation","validationCode":"function assertExplainTimeoutOptions(opts) {\n  if (opts.explain == null) return;\n  const hasTimeoutMS = opts.timeoutMS != null;\n  const hasMaxTimeMS = opts.maxTimeMS != null ||\n    (typeof opts.explain === 'object' && opts.explain.maxTimeMS != null);\n  if (hasTimeoutMS && hasMaxTimeMS) {\n    throw new Error('Cannot combine timeoutMS with explain+maxTimeMS on aggregate; remove one.');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await cursor.toArray();\n} catch (e) {\n  if (e instanceof MongoAPIError && /timeoutMS cannot be used with explain/.test(e.message)) {\n    // strip maxTimeMS and retry via cursor.explain()\n  } else throw e;\n}","preventionTips":["Always use cursor.explain(verbosity, { timeoutMS }) instead of mixing explain+timeoutMS in the constructor options.","Audit option builders for accidental maxTimeMS+timeoutMS co-occurrence."],"tags":["aggregation","explain","timeout","options-validation"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}