mongodb/node-mongodb-native · error · MongoAPIError

Cannot use maxTimeMS with timeoutMS for explain commands.

Error message

Cannot use maxTimeMS with timeoutMS for explain commands.

What it means

The driver's Client-Side Operation Timeout (CSOT) model treats timeoutMS as the overarching budget. For explain commands, specifying both maxTimeMS and timeoutMS is contradictory, so validateExplainTimeoutOptions (src/explain.ts:93) throws a MongoAPIError. This prevents two competing timeout semantics from being sent in the same command.

Solutions

  1. Remove maxTimeMS (both top-level and inside the explain option) when timeoutMS is set.
  2. Or remove timeoutMS for this call and rely solely on maxTimeMS.
  3. Avoid setting a global timeoutMS if you need per-command maxTimeMS control on explain queries.

Example fix

// before
await coll.find({}, { explain: { verbosity: 'queryPlanner', maxTimeMS: 1000 }, timeoutMS: 5000 }).toArray();
// after
await coll.find({}, { explain: { verbosity: 'queryPlanner' }, timeoutMS: 5000 }).toArray();
Defensive patterns

Strategy: validation

Validate before calling

function reconcileTimeoutOptions(options) {
  if (options?.timeoutMS != null) {
    delete options.maxTimeMS;
    if (options.explain && typeof options.explain === 'object') delete options.explain.maxTimeMS;
  }
  return options;
}

Type guard

function hasConflictingTimeouts(options: any): boolean {
  return options?.timeoutMS != null && (options?.maxTimeMS != null || options?.explain?.maxTimeMS != null);
}

Try / catch

try {
  await coll.find({}, opts).explain();
} catch (e) {
  if (e instanceof MongoAPIError && /maxTimeMS with timeoutMS/.test(e.message)) {
    // remove maxTimeMS and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling collection.find(..., { explain: { verbosity, maxTimeMS }, maxTimeMS, timeoutMS }) or aggregation explain with both maxTimeMS (either top-level or nested in explain) and timeoutMS set. Combining a globally configured timeoutMS with a per-call maxTimeMS on an explained command.

Common situations: Setting timeoutMS on the MongoClient and then passing maxTimeMS to a specific explain() call. Migrating from maxTimeMS to CSOT timeoutMS without removing the old maxTimeMS on explained queries.

Understand the failure class

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/da85d8bb7f9692a5. Report an issue: GitHub.

Appendix: source

Thrown at src/explain.ts:96

    this.maxTimeMS = maxTimeMS;
  }

  static fromOptions({ explain }: ExplainOptions = {}): Explain | undefined {
    if (explain == null) return;

    if (typeof explain === 'boolean' || typeof explain === 'string') {
      return new Explain(explain);
    }

    const { verbosity, maxTimeMS } = explain;
    return new Explain(verbosity, maxTimeMS);
  }
}

export function validateExplainTimeoutOptions(options: Document, explain?: Explain) {
  const { maxTimeMS, timeoutMS } = options;
  if (timeoutMS != null && (maxTimeMS != null || explain?.maxTimeMS != null)) {
    throw new MongoAPIError('Cannot use maxTimeMS with timeoutMS for explain commands.');
  }
}

/**
 * Applies an explain to a given command.
 * @internal
 *
 * @param command - the command on which to apply the explain
 * @param options - the options containing the explain verbosity
 */
export function decorateWithExplain(
  command: Document,
  explain: Explain
): {
  explain: Document;
  verbosity: ExplainVerbosity;
  maxTimeMS?: number;
} {

View on GitHub (pinned to dce7939f86)