Node.js "AbortError: The operation was aborted" - Fix Timeout Aborts in CI
An operation tied to an AbortSignal was cancelled - usually a timeout (AbortSignal.timeout) firing because a fetch or async task took longer than its budget. In CI this is often a slow network rather than a stuck operation.
What this error means
A fetch or other abortable call rejects with AbortError: The operation was aborted (code ABORT_ERR/ERR_ABORTED). It can be intermittent - passing on retry - when the cause is a transiently slow request hitting a tight timeout.
DOMException [AbortError]: The operation was aborted
at node:internal/deps/undici/undici:...
code: 20,
name: 'AbortError'Common causes
A timeout fired on a slow but legitimate request
A short AbortSignal.timeout(ms) cancels a fetch that was simply slow on a congested CI network. The operation would have succeeded with more time.
The signal was aborted by app logic
Code explicitly called controller.abort() (e.g. on shutdown or a superseded request) and a pending operation tied to that signal rejected as designed.
How to fix it
Raise the timeout and retry transient aborts
Give slow CI networks more headroom and retry timeout-driven aborts.
async function get(url) {
for (let i = 1; i <= 3; i++) {
try {
return await fetch(url, { signal: AbortSignal.timeout(15000) });
} catch (err) {
if (err.name !== 'AbortError' || i === 3) throw err;
}
}
}Distinguish timeout from intentional abort
- Check whether the abort came from your own
controller.abort()or from a timeout signal. - For intentional aborts (shutdown, superseded request), treat the rejection as expected, not an error.
- For timeouts, raise the budget or retry rather than failing the job.
How to prevent it
- Size
AbortSignal.timeoutbudgets for the slowest realistic CI network. - Retry timeout-driven aborts; do not retry intentional aborts.
- Cache or mock external calls in tests so they do not depend on live latency.