mongodb/node-mongodb-native · error · MongoAPIError

Cannot rewind cursor that does not own its timeout context.

Error message

Cannot rewind cursor that does not own its timeout context.

What it means

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.

Source

Thrown at src/cursor/abstract_cursor.ts:822

      throw new MongoTailableCursorError('Tailable cursor does not support batchSize');
    }

    if (typeof value !== 'number') {
      throw new MongoInvalidArgumentError('Operation "batchSize" requires an integer');
    }

    this.cursorOptions.batchSize = value;
    return this;
  }

  /**
   * Rewind this cursor to its uninitialized state. Any options that are present on the cursor will
   * remain in effect. Iterating this cursor will cause new queries to be sent to the server, even
   * if the resultant data has already been retrieved by this cursor.
   */
  rewind(): void {
    if (this.timeoutContext && this.timeoutContext.owner !== this) {
      throw new MongoAPIError(`Cannot rewind cursor that does not own its timeout context.`);
    }
    if (!this.initialized) {
      return;
    }

    this.cursorId = null;
    this.documents?.clear();
    this.timeoutContext?.clear();
    this.timeoutContext = undefined;
    this.isClosed = false;
    this.isKilled = false;
    this.initialized = false;
    this.hasEmittedClose = false;
    this.trackCursor();

    // We only want to end this session if we created it, and it hasn't ended yet
    if (this.cursorSession?.explicit === false) {
      if (!this.cursorSession.hasEnded) {

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Do not call rewind() on cursors whose timeoutContext is externally owned — construct a new cursor instead.
  2. If you need re-iteration, clone the cursor before iterating so the clone can be re-run.
  3. Drop the shared timeoutContext before rewinding, or own the context by passing timeoutMS at cursor level.

Example fix

// before
cursor.rewind(); // cursor's timeoutContext owned by session
// after
const fresh = collection.find(filter); // new cursor with its own context
Defensive patterns

Strategy: try-catch

Validate before calling

if (cursor.timeoutContext && cursor.timeoutContext.owner !== cursor) throw new Error('cannot rewind borrowed timeout context');

Try / catch

try {
  cursor.rewind();
} catch (e) {
  if (e.name === 'MongoAPIError' && /does not own its timeout context/.test(e.message)) {
    // build a fresh cursor instead
    cursor = collection.find(filter);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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