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
GridFSBucketWriteStream.abort() (src/gridfs/upload.ts:207) throws MongoAPIError 'Cannot call abort() on a stream twice' if abort has already been invoked (state.aborted === true). Abort is a one-time operation that deletes already-written chunks; calling it again is a logic error.
Solutions
- Track whether abort has been called with a boolean flag and skip subsequent calls.
- Use a single owner for the abort decision (either the AbortSignal handler or the finally block, not both).
- Catch this specific error and ignore it if defensive double-abort is unavoidable.
Example fix
// before
await s.abort();
await s.abort(); // throws
// after
let aborted = false;
async function safeAbort() { if (!aborted) { aborted = true; await s.abort(); } }
await safeAbort();
await safeAbort(); Defensive patterns
Strategy: validation
Validate before calling
function onceAbort(stream) {
let called = false;
return async () => {
if (called) return;
called = true;
return stream.abort();
};
} Type guard
function isStreamAborted(stream: GridFSBucketWriteStream): boolean {
return (stream as any).state?.aborted === true;
} Try / catch
try {
await stream.abort();
} catch (e) {
if (e instanceof MongoAPIError && /abort\(\) on a stream twice/.test(e.message)) {
// no-op: already aborted
} else throw e;
} Prevention
- Wrap abort in a single-call guard so multiple handlers share one invocation.
- Centralize cancellation in one owner (signal handler OR finally, not both).
- Track aborted state explicitly in your upload orchestration.
When it happens
Trigger: Calling uploadStream.abort() two or more times. Multiple cleanup handlers (e.g. signal listener + finally) both invoking abort on the same stream.
Common situations: An AbortSignal listener and a try/finally both calling abort. Retry/cancellation logic that does not track whether abort already fired.
Related errors
- Cannot abort a stream that has already completed
- 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/b89587543a1068bb.
Report an issue: GitHub.
Appendix: 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 dce7939f86)