{"id":"b4d94998d03ecd6b","repo":"mongodb/node-mongodb-native","slug":"cannot-rewind-cursor-that-does-not-own-its-timeout","errorCode":null,"errorMessage":"Cannot rewind cursor that does not own its timeout context.","messagePattern":"Cannot rewind cursor that does not own its timeout context\\.","errorType":"exception","errorClass":"MongoAPIError","httpStatus":null,"severity":"error","filePath":"src/cursor/abstract_cursor.ts","lineNumber":822,"sourceCode":"      throw new MongoTailableCursorError('Tailable cursor does not support batchSize');\n    }\n\n    if (typeof value !== 'number') {\n      throw new MongoInvalidArgumentError('Operation \"batchSize\" requires an integer');\n    }\n\n    this.cursorOptions.batchSize = value;\n    return this;\n  }\n\n  /**\n   * Rewind this cursor to its uninitialized state. Any options that are present on the cursor will\n   * remain in effect. Iterating this cursor will cause new queries to be sent to the server, even\n   * if the resultant data has already been retrieved by this cursor.\n   */\n  rewind(): void {\n    if (this.timeoutContext && this.timeoutContext.owner !== this) {\n      throw new MongoAPIError(`Cannot rewind cursor that does not own its timeout context.`);\n    }\n    if (!this.initialized) {\n      return;\n    }\n\n    this.cursorId = null;\n    this.documents?.clear();\n    this.timeoutContext?.clear();\n    this.timeoutContext = undefined;\n    this.isClosed = false;\n    this.isKilled = false;\n    this.initialized = false;\n    this.hasEmittedClose = false;\n    this.trackCursor();\n\n    // We only want to end this session if we created it, and it hasn't ended yet\n    if (this.cursorSession?.explicit === false) {\n      if (!this.cursorSession.hasEnded) {","sourceCodeStart":804,"sourceCodeEnd":840,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cursor/abstract_cursor.ts#L804-L840","documentation":"MongoAPIError thrown by cursor.rewind() when a timeoutContext exists but its owner is not this cursor. Cursors that share/borrow a CSOT timeout context (e.g. derived cursors, change streams with an explicit timeout context passed from a session) cannot rewind because resetting the timer would affect the owning context.","triggerScenarios":"Calling rewind() on a cursor constructed with an externally-supplied timeoutContext (timeoutContext.owner !== this), such as a cloned cursor or one bound to a session-level CSOT context. The guard `this.timeoutContext.owner !== this` catches this ownership mismatch.","commonSituations":"Using CSOT with sessions and then attempting rewind on a cursor whose timeout came from the operation/session; cloning cursors that carry a shared timeout context; calling rewind in a retry loop with borrowed contexts.","solutions":["Do not call rewind() on cursors whose timeoutContext is externally owned — construct a new cursor instead.","If you need re-iteration, clone the cursor before iterating so the clone can be re-run.","Drop the shared timeoutContext before rewinding, or own the context by passing timeoutMS at cursor level."],"exampleFix":"// before\ncursor.rewind(); // cursor's timeoutContext owned by session\n// after\nconst fresh = collection.find(filter); // new cursor with its own context","handlingStrategy":"try-catch","validationCode":"if (cursor.timeoutContext && cursor.timeoutContext.owner !== cursor) throw new Error('cannot rewind borrowed timeout context');","typeGuard":null,"tryCatchPattern":"try {\n  cursor.rewind();\n} catch (e) {\n  if (e.name === 'MongoAPIError' && /does not own its timeout context/.test(e.message)) {\n    // build a fresh cursor instead\n    cursor = collection.find(filter);\n  } else throw e;\n}","preventionTips":["Avoid rewind() on cursors derived from a session-level or borrowed CSOT context.","Build a fresh cursor (or clone) to re-run a query when the timeout is externally owned.","Pass timeoutMS at the cursor level so it owns its own context if you need rewind."],"tags":["cursor","csot","timeout","lifecycle"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}