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

  1. Remove maxTimeMS (or explain.maxTimeMS) from findOptions when timeoutMS is set.
  2. Or remove timeoutMS and keep maxTimeMS for the explain run.
  3. 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

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

Related errors


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)