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
- 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.
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
- 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.
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
- Cannot call abort() on a stream twice
- Options cannot be changed after the stream is initialized
- End option must be defined
- Start option must be defined
- Stream end ( ) must not be more than the length of the file…
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)