mongodb/node-mongodb-native · error · MongoInvalidArgumentError
Cannot set tailable cursor's timeoutMode to LIFETIME
Error message
Cannot set tailable cursor's timeoutMode to LIFETIME
What it means
Thrown as a MongoInvalidArgumentError in the AbstractCursor constructor when timeoutMS is set, the cursor is tailable, and the caller explicitly sets timeoutMode to CursorTimeoutMode.LIFETIME ('cursorLifetime'). A tailable cursor has an unbounded lifetime by definition, so applying a single deadline across the whole cursor would either kill it almost immediately or defeat the purpose of tailing; the driver therefore only permits ITERATION mode for tailable cursors when timeoutMS is in effect. The guard is hit only when the caller overrides the default, because tailable cursors default to ITERATION automatically.
Solutions
- Remove the explicit timeoutMode so the driver defaults the tailable cursor to ITERATION.
- If you genuinely need a bounded total lifetime, set timeoutMode: 'iteration' (or omit it) and enforce a wall-clock cap in your own loop.
- Re-evaluate whether the cursor needs to be tailable at all under a total-time budget.
Example fix
// before: tailable + explicit LIFETIME is rejected
const cursor = collection.find(filter, {
tailable: true,
awaitData: true,
timeoutMS: 1000,
timeoutMode: 'cursorLifetime'
});
// after: let the driver pick ITERATION (default for tailable)
const cursor = collection.find(filter, {
tailable: true,
awaitData: true,
timeoutMS: 1000
}); Defensive patterns
Strategy: validation
Validate before calling
// Enforce driver rule: tailable cursors may only use ITERATION mode
if (opts.tailable && opts.timeoutMS != null && opts.timeoutMode === 'cursorLifetime') {
throw new RangeError('Tailable cursors cannot use timeoutMode cursorLifetime; remove timeoutMode or use iteration.');
} Prevention
- For tailable cursors, omit timeoutMode and let the driver default to ITERATION.
- Do not reuse a non-tailable options object verbatim for a tailable cursor.
- Centralize cursor-option construction so the tailable/timeoutMode pairing is validated once.
When it happens
Trigger: collection.find(filter, { tailable: true, timeoutMS: 1000, timeoutMode: 'cursorLifetime' }); constructing a tailable change-stream-like cursor while trying to force a whole-cursor deadline.
Common situations: Copying timeoutMode from a non-tailable cursor config into a tailable one; reading the LIFETIME example in the JSDoc and applying it to a tailable cursor without noticing the tailable default is ITERATION.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Cannot set timeoutMode without setting timeoutMS
- Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable…
- Cannot use $out or $merge stage with ITERATION timeoutMode
- Tailable cursor does not support batchSize
- Argument for maxTimeMS must be a number
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/59990a3a3afd14e9.
Report an issue: GitHub.
Appendix: source
Thrown at src/cursor/abstract_cursor.ts:302
if (options.timeoutMode == null) {
if (options.tailable) {
if (options.awaitData) {
if (
options.maxAwaitTimeMS != null &&
options.maxAwaitTimeMS >= this.cursorOptions.timeoutMS
)
throw new MongoInvalidArgumentError(
'Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable awaitData cursor'
);
}
this.cursorOptions.timeoutMode = CursorTimeoutMode.ITERATION;
} else {
this.cursorOptions.timeoutMode = CursorTimeoutMode.LIFETIME;
}
} else {
if (options.tailable && options.timeoutMode === CursorTimeoutMode.LIFETIME) {
throw new MongoInvalidArgumentError(
"Cannot set tailable cursor's timeoutMode to LIFETIME"
);
}
this.cursorOptions.timeoutMode = options.timeoutMode;
}
} else {
if (options.timeoutMode != null)
throw new MongoInvalidArgumentError('Cannot set timeoutMode without setting timeoutMS');
}
// Set for initial command
this.cursorOptions.omitMaxTimeMS =
this.cursorOptions.timeoutMS != null &&
((this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION &&
!this.cursorOptions.tailable) ||
(this.cursorOptions.tailable && !this.cursorOptions.awaitData));
const readConcern = ReadConcern.fromOptions(options);View on GitHub (pinned to dce7939f86)