mongodb/node-mongodb-native · error · MongoCursorInUseError

Cursor is already initialized

Error message

Cursor is already initialized

What it means

Thrown as a MongoCursorInUseError (default message 'Cursor is already initialized') from throwIfInitialized() once `this.initialized` is true. Initialization becomes true during the first cursorInit() (set even if the first command fails), after which the cursor has a server-side cursor id, a session, and possibly a timeout context. Many configuration methods (addCursorFlag, map, withReadPreference, withReadConcern, maxTimeMS, batchSize, addStage) call throwIfInitialized() to forbid changing options after the wire command has already been sent, because changing them would have no effect or desync the client from the server.

Solutions

  1. Set all options before the first call that triggers iteration (next/hasNext/toArray/for-await).
  2. If the cursor has already run, create a new cursor via collection.find()/aggregate() with the desired options.
  3. Use cursor.rewind() (when allowed) to reset to the uninitialized state, then reconfigure before iterating again.
  4. Build the full options object up front instead of chaining config calls across await boundaries.

Example fix

// before: config after iteration started
const cursor = collection.find({});
await cursor.next();
cursor.batchSize(50); // throws MongoCursorInUseError

// after: configure before iterating
const cursor = collection.find({}, { batchSize: 50 });
await cursor.next();
Defensive patterns

Strategy: validation

Validate before calling

// Configure fully before iterating; recreate (or rewind) to change options later
function buildCursor(filter: object, opts: FindOptions) {
  const cursor = collection.find(filter, opts); // all options up front
  // do NOT call addCursorFlag/batchSize/maxTimeMS/map after any await on cursor
  return cursor;
}

Try / catch

try {
  cursor.batchSize(50);
} catch (err) {
  if (err instanceof MongoCursorInUseError) {
    // cursor already started: build a fresh one with the new options
    cursor = collection.find(filter, { batchSize: 50 });
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling cursor.addCursorFlag(...), cursor.batchSize(...), cursor.maxTimeMS(...), cursor.withReadPreference(...), cursor.map(...), or aggregation cursor.addStage(...) after cursor.next(), hasNext(), tryNext(), toArray(), or a for-await loop has started; calling a config method after an earlier iteration attempt failed (initialized is set even on failure).

Common situations: Configuring a cursor lazily inside an iteration loop; reusing a cursor variable and trying to change options between runs instead of creating a new cursor; calling map() after a partial read; the first command failed and the user retries with a tweaked option on the same cursor instance.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/acc895fcdff9d4a5. Report an issue: GitHub.

Appendix: source

Thrown at src/cursor/abstract_cursor.ts:1091

      if (transformedDocument === null) {
        const TRANSFORM_TO_NULL_ERROR =
          'Cursor returned a `null` document, but the cursor is not exhausted.  Mapping documents to `null` is not supported in the cursor transform.';
        throw new MongoAPIError(TRANSFORM_TO_NULL_ERROR);
      }
      return transformedDocument;
    } catch (transformError) {
      try {
        await this.close();
      } catch (closeError) {
        squashError(closeError);
      }
      throw transformError;
    }
  }

  /** @internal */
  protected throwIfInitialized() {
    if (this.initialized) throw new MongoCursorInUseError();
  }
}

class ReadableCursorStream extends Readable {
  private _cursor: AbstractCursor;
  private _readInProgress = false;

  constructor(cursor: AbstractCursor) {
    super({
      objectMode: true,
      autoDestroy: false,
      highWaterMark: 1
    });
    this._cursor = cursor;
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  override _read(size: number): void {

View on GitHub (pinned to dce7939f86)