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

  1. Catch the error and branch on err.name === 'AbortError' (or err.code === 'ABORT_ERR') to treat it as cancellation, not failure
  2. Check controller.signal.aborted before starting the read when the controller may already be cancelled
  3. Use a fresh AbortController per read instead of reusing one shared controller
  4. 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

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


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/a6a673e67eb995f8. Report an issue: GitHub.