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 when a tailable cursor is constructed with timeoutMode explicitly set to LIFETIME. Tailable cursors are incompatible with a single lifetime timeout budget — they must use ITERATION mode so each getMore gets a fresh time slice. This guard fires only when the caller explicitly passes timeoutMode.
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 3366c21a63)
Solutions
- Omit timeoutMode for tailable cursors — the driver auto-selects ITERATION when tailable+awaitData and timeoutMS is set.
- If you must pass it explicitly, use CursorTimeoutMode.ITERATION for any tailable cursor.
- Remove tailable if you genuinely want a LIFETIME-bounded query.
Example fix
// before
collection.find(filter, { tailable: true, awaitData: true, timeoutMS: 1000, timeoutMode: CursorTimeoutMode.LIFETIME });
// after
collection.find(filter, { tailable: true, awaitData: true, timeoutMS: 1000, timeoutMode: CursorTimeoutMode.ITERATION }); Defensive patterns
Strategy: validation
Validate before calling
function checkTimeoutMode(opts) {
if (opts.tailable && opts.timeoutMode === 'lifetime') return false;
return true;
} Prevention
- Omit timeoutMode for tailable cursors — the driver selects ITERATION automatically.
- Never share a generic 'lifetime timeout' config across all cursor types.
- Audit option helpers that inject timeoutMode unconditionally.
When it happens
Trigger: Passing { tailable: true, timeoutMS: 1000, timeoutMode: CursorTimeoutMode.LIFETIME } together. The combination is contradictory because tailable cursors are long-lived by design.
Common situations: Hardcoding timeoutMode from a shared config helper that defaults to LIFETIME; migrating to CSOT and forgetting that tailable cursors need ITERATION mode.
Related errors
- Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable aw
- Cannot set timeoutMode without setting timeoutMS
- Argument for maxTimeMS must be a number
- Tailable cursor does not support batchSize
- Cannot rewind cursor that does not own its timeout context.
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/59990a3a3afd14e9.json.
Report an issue: GitHub.