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
- Guard with your own boolean: if (!aborted) { aborted = true; await stream.abort(); }
- Remove duplicate abort calls across event handlers and centralize cancellation.
- 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
- Use a single guarded abort() wrapper
- Avoid firing abort from multiple independent handlers
- Make cancellation logic idempotent
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
- Cannot abort a stream that has already completed
- Options cannot be changed after the stream is initialized
- Stream start (${options.start}) must not be more than the le
- Stream start (${options.start}) must not be negative
- Stream start (${options.start}) must not be greater than str
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/b89587543a1068bb.json.
Report an issue: GitHub.