{"id":"edf80cdfbadc4f81","repo":"mongodb/node-mongodb-native","slug":"cannot-use-out-or-merge-stage-with-iteration-tim","errorCode":null,"errorMessage":"Cannot use $out or $merge stage with ITERATION timeoutMode","messagePattern":"Cannot use \\$out or \\$merge stage with ITERATION timeoutMode","errorType":"exception","errorClass":"MongoAPIError","httpStatus":null,"severity":"error","filePath":"src/cursor/aggregation_cursor.ts","lineNumber":57,"sourceCode":"  constructor(\n    client: MongoClient,\n    namespace: MongoDBNamespace,\n    pipeline: Document[] = [],\n    options: AggregateOptions & Abortable = {}\n  ) {\n    super(client, namespace, options);\n\n    this.pipeline = pipeline;\n    this.aggregateOptions = options;\n\n    const lastStage: Document | undefined = this.pipeline[this.pipeline.length - 1];\n\n    if (\n      this.cursorOptions.timeoutMS != null &&\n      this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION &&\n      (lastStage?.$merge != null || lastStage?.$out != null)\n    )\n      throw new MongoAPIError('Cannot use $out or $merge stage with ITERATION timeoutMode');\n  }\n\n  clone(): AggregationCursor<TSchema> {\n    const clonedOptions = mergeOptions({}, this.aggregateOptions);\n    delete clonedOptions.session;\n    return new AggregationCursor(this.client, this.namespace, this.pipeline, {\n      ...clonedOptions\n    });\n  }\n\n  override map<T>(transform: (doc: TSchema) => T): AggregationCursor<T> {\n    return super.map(transform) as AggregationCursor<T>;\n  }\n\n  /** @internal */\n  async _initialize(session: ClientSession): Promise<InitialCursorResponse> {\n    const options = {\n      ...this.aggregateOptions,","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cursor/aggregation_cursor.ts#L39-L75","documentation":"Thrown by the AggregationCursor constructor (MongoAPIError) when timeoutMS is set, timeoutMode is ITERATION, and the final pipeline stage is $merge or $out. ITERATION mode resets the timeout per getMore batch, but $out/$merge write the entire result set in one shot on the first batch, so a per-iteration timeout cannot meaningfully bound them. The driver rejects the combination at construction time.","triggerScenarios":"collection.aggregate(pipeline, { timeoutMS: N, timeoutMode: 'iteration' }) where pipeline ends with { $out: 'coll' } or { $merge: ... }. Also triggered if you pass timeoutMS without timeoutMode on a non-tailable aggregation, because the driver defaults non-tailable+timeoutMS to LIFETIME — but explicitly setting 'iteration' with $out/$merge is the direct hit.","commonSituations":"Building a materialized view with $out/$merge under a global timeout policy; inheriting timeoutMS from a shared options object and forcing iteration mode.","solutions":["Omit timeoutMode (or set it to 'cursorLifetime') when the pipeline ends with $out/$merge","Remove timeoutMS for $out/$merge pipelines, or wrap the whole operation in your own Promise.race timeout","If you need a per-batch bound, restructure the pipeline so the write stage is not the terminal stage"],"exampleFix":"// before\ncoll.aggregate([{ ... }, { $out: 'result' }], { timeoutMS: 5000, timeoutMode: 'iteration' });\n// after\ncoll.aggregate([{ ... }, { $out: 'result' }], { timeoutMS: 5000 }); // LIFETIME (default)","handlingStrategy":"validation","validationCode":"function safeAggregate(coll, pipeline, opts) {\n  const last = pipeline[pipeline.length - 1];\n  const hasWriteStage = last && (last.$out != null || last.$merge != null);\n  if (opts?.timeoutMS != null && opts?.timeoutMode === 'iteration' && hasWriteStage) {\n    const { timeoutMode, ...rest } = opts;\n    return coll.aggregate(pipeline, rest); // drop iteration mode\n  }\n  return coll.aggregate(pipeline, opts);\n}","typeGuard":"const isWriteStage = (s) => s != null && (s.$out != null || s.$merge != null);\nconst canUseIterationMode = (pipeline) => !isWriteStage(pipeline[pipeline.length - 1]);","tryCatchPattern":null,"preventionTips":["Do not combine timeoutMode:'iteration' with $out/$merge","Default to 'cursorLifetime' (or omit timeoutMode) for materialization pipelines","Validate the terminal stage before constructing the cursor"],"tags":["aggregation","timeout","timeoutms","out","merge"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}