{"record":{"id":"acc895fcdff9d4a5","repo":"mongodb/node-mongodb-native","slug":"cursor-is-already-initialized","errorCode":null,"errorMessage":"Cursor is already initialized","messagePattern":"Cursor is already initialized","errorType":"exception","errorClass":"MongoCursorInUseError","httpStatus":null,"severity":"error","filePath":"src/cursor/abstract_cursor.ts","lineNumber":1091,"sourceCode":"      if (transformedDocument === null) {\n        const TRANSFORM_TO_NULL_ERROR =\n          'Cursor returned a `null` document, but the cursor is not exhausted.  Mapping documents to `null` is not supported in the cursor transform.';\n        throw new MongoAPIError(TRANSFORM_TO_NULL_ERROR);\n      }\n      return transformedDocument;\n    } catch (transformError) {\n      try {\n        await this.close();\n      } catch (closeError) {\n        squashError(closeError);\n      }\n      throw transformError;\n    }\n  }\n\n  /** @internal */\n  protected throwIfInitialized() {\n    if (this.initialized) throw new MongoCursorInUseError();\n  }\n}\n\nclass ReadableCursorStream extends Readable {\n  private _cursor: AbstractCursor;\n  private _readInProgress = false;\n\n  constructor(cursor: AbstractCursor) {\n    super({\n      objectMode: true,\n      autoDestroy: false,\n      highWaterMark: 1\n    });\n    this._cursor = cursor;\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  override _read(size: number): void {","sourceCodeStart":1073,"sourceCodeEnd":1109,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/cursor/abstract_cursor.ts#L1073-L1109","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Set all options before the first call that triggers iteration (next/hasNext/toArray/for-await).","If the cursor has already run, create a new cursor via collection.find()/aggregate() with the desired options.","Use cursor.rewind() (when allowed) to reset to the uninitialized state, then reconfigure before iterating again.","Build the full options object up front instead of chaining config calls across await boundaries."],"exampleFix":"// before: config after iteration started\nconst cursor = collection.find({});\nawait cursor.next();\ncursor.batchSize(50); // throws MongoCursorInUseError\n\n// after: configure before iterating\nconst cursor = collection.find({}, { batchSize: 50 });\nawait cursor.next();","handlingStrategy":"validation","validationCode":"// Configure fully before iterating; recreate (or rewind) to change options later\nfunction buildCursor(filter: object, opts: FindOptions) {\n  const cursor = collection.find(filter, opts); // all options up front\n  // do NOT call addCursorFlag/batchSize/maxTimeMS/map after any await on cursor\n  return cursor;\n}","typeGuard":null,"tryCatchPattern":"try {\n  cursor.batchSize(50);\n} catch (err) {\n  if (err instanceof MongoCursorInUseError) {\n    // cursor already started: build a fresh one with the new options\n    cursor = collection.find(filter, { batchSize: 50 });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Set every option before the first call that triggers iteration.","Create a new cursor instead of reconfiguring a started one.","Use rewind() only when no shared timeout context is in play.","Build the options object completely up front."],"tags":["cursor","initialized","in-use","configuration","logic-error"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}