denoland/deno · error · AbortError
ABORT_ERR
ABORT_ERR
Error message
The operation was aborted
What it means
Thrown by the callback-style fs.readFile wrapper in Deno's node:fs polyfill when the AbortSignal passed via options.signal is aborted. The finally block removes the abort handler and calls signal.throwIfAborted(), so an aborted readFile always rejects with an AbortError (code ABORT_ERR) instead of a partial result. This matches Node semantics: cancelling the read is an error, not a silent success.
Source
Thrown at ext/node/polyfills/fs.ts:701
const data = await op_fs_read_file_async(
path,
cancelRid,
flagsNumber,
);
return data;
} finally {
if (options?.signal) {
options.signal[abortSignal.remove](abortHandler);
// always throw the abort error when aborted
options.signal.throwIfAborted();
}
}
}
function readFileCheckAborted(signal: AbortSignal | undefined) {
if (signal?.aborted) {
throw new AbortError(undefined, { cause: signal.reason });
}
}
function readFileConcatBuffers(buffers: Uint8Array[]): Uint8Array {
let totalLen = 0;
for (let i = 0; i < buffers.length; ++i) {
totalLen += TypedArrayPrototypeGetByteLength(buffers[i]);
}
const contents = new Uint8Array(totalLen);
let n = 0;
for (let i = 0; i < buffers.length; ++i) {
const buf = buffers[i];
TypedArrayPrototypeSet(contents, buf, n);
n += TypedArrayPrototypeGetByteLength(buf);
}
return contents;View on GitHub (pinned to 89f33cbef2)
Solutions
- Catch the error and branch on err.name === 'AbortError' (or err.code === 'ABORT_ERR') to treat it as cancellation, not failure
- Check controller.signal.aborted before starting the read when the controller may already be cancelled
- Use a fresh AbortController per read instead of reusing one shared controller
- If you never intend to cancel, remove the signal option
Example fix
// before
const data = await fs.promises.readFile(path, { signal }); // throws AbortError uncaught
// after
try {
const data = await fs.promises.readFile(path, { signal });
} catch (err) {
if (err.name === 'AbortError') return; // cancelled on purpose
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (signal?.aborted) {
// do not start the read; it would immediately throw AbortError
return;
} Type guard
const isAbortError = (e: unknown): e is Error =>
e instanceof Error && (e as Error & { code?: string }).code === 'ABORT_ERR'; Try / catch
try {
const data = await fs.promises.readFile(path, { signal });
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') return; // intentional cancel
throw err;
} Prevention
- Check signal.aborted before each readFile when reusing one controller in loops
- Use a dedicated AbortController per file operation so unrelated aborts do not cascade
- Treat AbortError as control flow (name === 'AbortError'), never as a generic failure
When it happens
Trigger: Calling fs.readFile(path, { signal }, cb) or fs.promises.readFile(path, { signal }) and calling controller.abort() while the read is in flight; also aborting the controller before the promise settles (throwIfAborted re-fires in the finally block).
Common situations: Request handlers that race file reads against client disconnects; download timeouts that share one AbortController across fetch and fs reads; reusing an already-aborted controller for a retry loop.
Related errors
- ERR_FS_CP_UNKNOWN
- ERR_INVALID_ARG_TYPE
- ERR_INVALID_RETURN_VALUE
- ERR_INVALID_ARG_TYPE
- ERR_FS_FILE_TOO_LARGE
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/a6a673e67eb995f8.
Report an issue: GitHub.