mongodb/node-mongodb-native · error · MongoOperationTimeoutError

Expired after ms

Error message

Expired after ${this.timeoutMS}ms

What it means

Thrown by CSOTTimeoutContext.getRemainingTimeMSOrThrow() when the remaining time budget is zero or negative, i.e. the operation's total timeoutMS has been exhausted. This is the canonical CSOT (Client-Side Operation Timeout) expiry error, raised as a MongoOperationTimeoutError with the configured timeoutMS value in the message.

Solutions

  1. Increase the timeoutMS value on the client, session, or operation to fit the workload's actual duration.
  2. Optimize the query (add indexes, reduce result size, use $project) so it completes within the current budget.
  3. For transactions, ensure withTransaction's total operations fit within the configured timeoutMS.

Example fix

// before
collection.aggregate(pipeline, { timeoutMS: 1000 }); // too tight

// after
collection.aggregate(pipeline, { timeoutMS: 30000 });
Defensive patterns

Strategy: retry

Type guard

import { MongoOperationTimeoutError } from 'mongodb';
function isCSOTTimeout(e: unknown): e is MongoOperationTimeoutError {
  return e instanceof MongoOperationTimeoutError || (e instanceof Error && /Expired after/.test(e.message));
}

Try / catch

async function runWithBackoff<T>(fn: () => Promise<T>, retries = 2, baseMs = 1000): Promise<T> {
  for (let i = 0; i <= retries; i++) {
    try { return await fn(); }
    catch (e) { if (isCSOTTimeout(e) && i < retries) { await new Promise(r => setTimeout(r, baseMs * 2 ** i)); continue; } throw e; }
  }
  throw new Error('unreachable');
}

Prevention

When it happens

Trigger: Any operation running under CSOT (timeoutMS set on the client, session, or operation) where the elapsed time since the context started exceeds timeoutMS. Surfaces during server selection, command execution, cursor getMore, or transaction commit/abort when the deadline has passed.

Common situations: Long-running aggregations or queries against slow clusters with a tight timeoutMS. Also common in withTransaction() callbacks whose total work exceeds the transaction's timeoutMS, or when retries consume the remaining budget.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src/timeout.ts:314

    this.minRoundTripTime = 0;
    this._serverSelectionTimeout?.clear();
    this._connectionCheckoutTimeout?.clear();
  }

  clear(): void {
    this._serverSelectionTimeout?.clear();
    this._connectionCheckoutTimeout?.clear();
  }

  /**
   * @internal
   * Throws a MongoOperationTimeoutError if the context has expired.
   * If the context has not expired, returns the `remainingTimeMS`
   **/
  getRemainingTimeMSOrThrow(message?: string): number {
    const { remainingTimeMS } = this;
    if (remainingTimeMS <= 0)
      throw new MongoOperationTimeoutError(message ?? `Expired after ${this.timeoutMS}ms`);
    return remainingTimeMS;
  }

  /**
   * @internal
   * This method is intended to be used in situations where concurrent operation are on the same deadline, but cannot share a single `TimeoutContext` instance.
   * Returns a new instance of `CSOTTimeoutContext` constructed with identical options, but setting the `start` property to `this.start`.
   */
  clone(): CSOTTimeoutContext {
    const timeoutContext = new CSOTTimeoutContext({
      timeoutMS: this.timeoutMS,
      serverSelectionTimeoutMS: this.serverSelectionTimeoutMS
    });
    timeoutContext.start = this.start;
    return timeoutContext;
  }

  override refreshed(): CSOTTimeoutContext {

View on GitHub (pinned to dce7939f86)