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

  1. Remove maxTimeMS from aggregateOptions and rely on timeoutMS alone for the explain.
  2. Or remove timeoutMS and keep maxTimeMS for the explain.
  3. Prefer cursor.explain(verbosity, { timeoutMS }) which resolves explain/timeout options correctly via resolveExplainTimeoutOptions.
  4. 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

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

Related errors


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)