{"record":{"id":"222a5a6dd394a296","repo":"mongodb/node-mongodb-native","slug":"cannot-abort-a-stream-that-has-already-completed","errorCode":null,"errorMessage":"Cannot abort a stream that has already completed","messagePattern":"Cannot abort a stream that has already completed","errorType":"exception","errorClass":"MongoAPIError","httpStatus":null,"severity":"error","filePath":"src/gridfs/upload.ts","lineNumber":204,"sourceCode":"  }\n\n  /** @internal */\n  override _final(callback: (error?: Error | null) => void): void {\n    if (this.state.streamEnd) {\n      return queueMicrotask(callback);\n    }\n    this.state.streamEnd = true;\n    writeRemnant(this, callback);\n  }\n\n  /**\n   * Places this write stream into an aborted state (all future writes fail)\n   * and deletes all chunks that have already been written.\n   */\n  async abort(): Promise<void> {\n    if (this.state.streamEnd) {\n      // TODO(NODE-3485): Replace with MongoGridFSStreamClosed\n      throw new MongoAPIError('Cannot abort a stream that has already completed');\n    }\n\n    if (this.state.aborted) {\n      // TODO(NODE-3485): Replace with MongoGridFSStreamClosed\n      throw new MongoAPIError('Cannot call abort() on a stream twice');\n    }\n\n    this.state.aborted = true;\n    const remainingTimeMS = this.timeoutContext?.getRemainingTimeMSOrThrow(\n      `Upload timed out after ${this.timeoutContext?.timeoutMS}ms`\n    );\n\n    await this.chunks.deleteMany({ files_id: this.id }, { timeoutMS: remainingTimeMS });\n  }\n}\n\nfunction handleError(stream: GridFSBucketWriteStream, error: Error, callback: Callback): void {\n  if (stream.state.errored) {","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/gridfs/upload.ts#L186-L222","documentation":"GridFSBucketWriteStream.abort() (src/gridfs/upload.ts:201) throws MongoAPIError 'Cannot abort a stream that has already completed' if called after the stream has finished (streamEnd === true). Once a write stream has emitted 'finish', its chunks are finalized and the file document is committed; aborting is no longer meaningful.","triggerScenarios":"Calling uploadStream.abort() after the stream has finished writing (after 'finish' event or after stream.end() completed). Calling abort in a cleanup/finally handler that runs after a successful upload.","commonSituations":"A finally block that aborts unconditionally on both success and failure paths. Race between an in-flight upload completing and a timeout/cancellation triggering abort.","solutions":["Track upload completion with a 'finish' listener and only call abort() when the upload has not yet finished.","In finally/cleanup handlers, guard abort with a check of the stream's done/finished state.","Catch this specific error and treat it as a no-op when aborting defensively."],"exampleFix":"// before\nconst s = bucket.openUploadStream('f');\ns.end(buffer);\nawait s.abort(); // throws\n// after\nconst s = bucket.openUploadStream('f');\nlet finished = false;\ns.on('finish', () => { finished = true; });\ns.end(buffer);\nif (!finished) await s.abort();","handlingStrategy":"validation","validationCode":"function abortIfActive(stream, state) {\n  if (state.finished || stream.done) return Promise.resolve();\n  return stream.abort();\n}","typeGuard":"function isStreamFinished(stream: GridFSBucketWriteStream): boolean {\n  return stream.done === true || (stream as any).state?.streamEnd === true;\n}","tryCatchPattern":"try {\n  await stream.abort();\n} catch (e) {\n  if (e instanceof MongoAPIError && /already completed/.test(e.message)) {\n    // no-op: upload finished already\n  } else throw e;\n}","preventionTips":["Track 'finish' with a listener and only abort when not finished.","Guard abort calls in finally blocks with a finished check.","Avoid racing cancellation signals against stream completion."],"tags":["gridfs","stream","lifecycle"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}