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

  1. Omit timeoutMode for tailable cursors — the driver auto-selects ITERATION when tailable+awaitData and timeoutMS is set.
  2. If you must pass it explicitly, use CursorTimeoutMode.ITERATION for any tailable cursor.
  3. 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

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


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/59990a3a3afd14e9.json. Report an issue: GitHub.