mongodb/node-mongodb-native · error · MongoAPIError

Cannot call abort() on a stream twice

Error message

Cannot call abort() on a stream twice

What it means

Thrown by GridFSBucketWriteStream.abort() when state.aborted is already true, i.e. abort() was invoked more than once on the same upload stream. After the first abort sets state.aborted = true and deletes written chunks, a second abort has nothing to do and is rejected as a MongoAPIError (NODE-3485 tracks a dedicated type).

Source

Thrown at src/gridfs/upload.ts:209

      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) {
    queueMicrotask(callback);
    return;
  }
  stream.state.errored = true;
  queueMicrotask(() => callback(error));

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Guard with your own boolean: if (!aborted) { aborted = true; await stream.abort(); }
  2. Remove duplicate abort calls across event handlers and centralize cancellation.
  3. Catch MongoAPIError around abort and ignore the double-abort case when idempotent cancellation is intended.

Example fix

// before
stream.on('error', () => stream.abort());
setTimeout(() => stream.abort(), 1000); // double-abort risk

// after
let aborted = false;
const safeAbort = async () => {
  if (aborted) return;
  aborted = true;
  await stream.abort();
};
stream.on('error', safeAbort);
setTimeout(safeAbort, 1000);
Defensive patterns

Strategy: validation

Validate before calling

let aborted = false;
async function safeAbort() {
  if (aborted) return;
  aborted = true;
  await stream.abort();
}

Try / catch

try { await stream.abort(); } catch (e) {
  if (e instanceof MongoAPIError && /twice/.test(e.message)) return;
  throw e;
}

Prevention

When it happens

Trigger: Calling uploadStream.abort() twice in sequence; concurrent abort() calls (e.g. from a timeout handler and an error handler firing near-simultaneously); retry logic that re-invokes abort on failure.

Common situations: Multiple cleanup paths (timeout + manual cancel); error handlers that call abort which a prior handler already triggered; promise wrappers that retry abort.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/b89587543a1068bb.json. Report an issue: GitHub.