mongodb/node-mongodb-native · error · MongoAPIError

Cannot abort a stream that has already completed

Error message

Cannot abort a stream that has already completed

What it means

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.

Solutions

  1. Track upload completion with a 'finish' listener and only call abort() when the upload has not yet finished.
  2. In finally/cleanup handlers, guard abort with a check of the stream's done/finished state.
  3. Catch this specific error and treat it as a no-op when aborting defensively.

Example fix

// before
const s = bucket.openUploadStream('f');
s.end(buffer);
await s.abort(); // throws
// after
const s = bucket.openUploadStream('f');
let finished = false;
s.on('finish', () => { finished = true; });
s.end(buffer);
if (!finished) await s.abort();
Defensive patterns

Strategy: validation

Validate before calling

function abortIfActive(stream, state) {
  if (state.finished || stream.done) return Promise.resolve();
  return stream.abort();
}

Type guard

function isStreamFinished(stream: GridFSBucketWriteStream): boolean {
  return stream.done === true || (stream as any).state?.streamEnd === true;
}

Try / catch

try {
  await stream.abort();
} catch (e) {
  if (e instanceof MongoAPIError && /already completed/.test(e.message)) {
    // no-op: upload finished already
  } else throw e;
}

Prevention

When it happens

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

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

Related errors


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

Appendix: source

Thrown at src/gridfs/upload.ts:204

  }

  /** @internal */
  override _final(callback: (error?: Error | null) => void): void {
    if (this.state.streamEnd) {
      return queueMicrotask(callback);
    }
    this.state.streamEnd = true;
    writeRemnant(this, callback);
  }

  /**
   * Places this write stream into an aborted state (all future writes fail)
   * and deletes all chunks that have already been written.
   */
  async abort(): Promise<void> {
    if (this.state.streamEnd) {
      // TODO(NODE-3485): Replace with MongoGridFSStreamClosed
      throw new MongoAPIError('Cannot abort a stream that has already completed');
    }

    if (this.state.aborted) {
      // TODO(NODE-3485): Replace with MongoGridFSStreamClosed
      throw new MongoAPIError('Cannot call abort() on a stream twice');
    }

    this.state.aborted = true;
    const remainingTimeMS = this.timeoutContext?.getRemainingTimeMSOrThrow(
      `Upload timed out after ${this.timeoutContext?.timeoutMS}ms`
    );

    await this.chunks.deleteMany({ files_id: this.id }, { timeoutMS: remainingTimeMS });
  }
}

function handleError(stream: GridFSBucketWriteStream, error: Error, callback: Callback): void {
  if (stream.state.errored) {

View on GitHub (pinned to dce7939f86)