{"record":{"id":"59990a3a3afd14e9","repo":"mongodb/node-mongodb-native","slug":"cannot-set-tailable-cursor-s-timeoutmode-to-lifeti","errorCode":null,"errorMessage":"Cannot set tailable cursor's timeoutMode to LIFETIME","messagePattern":"Cannot set tailable cursor's timeoutMode to LIFETIME","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/cursor/abstract_cursor.ts","lineNumber":302,"sourceCode":"      if (options.timeoutMode == null) {\n        if (options.tailable) {\n          if (options.awaitData) {\n            if (\n              options.maxAwaitTimeMS != null &&\n              options.maxAwaitTimeMS >= this.cursorOptions.timeoutMS\n            )\n              throw new MongoInvalidArgumentError(\n                'Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable awaitData cursor'\n              );\n          }\n\n          this.cursorOptions.timeoutMode = CursorTimeoutMode.ITERATION;\n        } else {\n          this.cursorOptions.timeoutMode = CursorTimeoutMode.LIFETIME;\n        }\n      } else {\n        if (options.tailable && options.timeoutMode === CursorTimeoutMode.LIFETIME) {\n          throw new MongoInvalidArgumentError(\n            \"Cannot set tailable cursor's timeoutMode to LIFETIME\"\n          );\n        }\n        this.cursorOptions.timeoutMode = options.timeoutMode;\n      }\n    } else {\n      if (options.timeoutMode != null)\n        throw new MongoInvalidArgumentError('Cannot set timeoutMode without setting timeoutMS');\n    }\n\n    // Set for initial command\n    this.cursorOptions.omitMaxTimeMS =\n      this.cursorOptions.timeoutMS != null &&\n      ((this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION &&\n        !this.cursorOptions.tailable) ||\n        (this.cursorOptions.tailable && !this.cursorOptions.awaitData));\n\n    const readConcern = ReadConcern.fromOptions(options);","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/cursor/abstract_cursor.ts#L284-L320","documentation":"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.","triggerScenarios":"collection.find(filter, { tailable: true, timeoutMS: 1000, timeoutMode: 'cursorLifetime' }); constructing a tailable change-stream-like cursor while trying to force a whole-cursor deadline.","commonSituations":"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.","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."],"exampleFix":"// before: tailable + explicit LIFETIME is rejected\nconst cursor = collection.find(filter, {\n  tailable: true,\n  awaitData: true,\n  timeoutMS: 1000,\n  timeoutMode: 'cursorLifetime'\n});\n\n// after: let the driver pick ITERATION (default for tailable)\nconst cursor = collection.find(filter, {\n  tailable: true,\n  awaitData: true,\n  timeoutMS: 1000\n});","handlingStrategy":"validation","validationCode":"// Enforce driver rule: tailable cursors may only use ITERATION mode\nif (opts.tailable && opts.timeoutMS != null && opts.timeoutMode === 'cursorLifetime') {\n  throw new RangeError('Tailable cursors cannot use timeoutMode cursorLifetime; remove timeoutMode or use iteration.');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["cursor","tailable","csot","timeout-mode","invalid-argument"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}