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
- Increase the timeoutMS value on the client, session, or operation to fit the workload's actual duration.
- Optimize the query (add indexes, reduce result size, use $project) so it completes within the current budget.
- 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
- Profile slow queries and set timeoutMS comfortably above observed p99 latency.
- Add indexes for query predicates to keep operations within the CSOT budget.
- For transactions, budget timeoutMS for the sum of all inner operations, not just one.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- An operation cannot be given a timeoutMS setting when…
- Cannot set timeoutMode without setting timeoutMS
- Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable…
- Cannot use maxTimeMS with timeoutMS for explain commands.
- KMS request timed out
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)