Skip to content

fix(xhr): unsubscribe cancelToken and signal on error, timeout, and abort paths#10787

Merged
jasonsaayman merged 3 commits intoaxios:v1.xfrom
iruizsalinas:fix/xhr-cancel-listener-leak-on-errors
Apr 25, 2026
Merged

fix(xhr): unsubscribe cancelToken and signal on error, timeout, and abort paths#10787
jasonsaayman merged 3 commits intoaxios:v1.xfrom
iruizsalinas:fix/xhr-cancel-listener-leak-on-errors

Conversation

@iruizsalinas
Copy link
Copy Markdown
Contributor

@iruizsalinas iruizsalinas commented Apr 21, 2026

The xhr adapter's onabort, onerror, ontimeout, and onCanceled handlers reject the promise but never run done(), so the listener they added to cancelToken or AbortSignal stays attached. If you reuse one token or controller for many requests, this turns into a memory leak: every failed request retains a listener holding the full config.

Quick repro:

const { XMLHttpRequest } = require('xmlhttprequest');
globalThis.XMLHttpRequest = XMLHttpRequest;
const axios = require('axios');

(async () => {
  const source = axios.CancelToken.source();
  for (let i = 0; i < 500; i++) {
    await axios.get('http://127.0.0.1:1/', {
      cancelToken: source.token,
      adapter: 'xhr',
      timeout: 500,
      data: { blob: new Uint8Array(100_000).fill(i & 0xff) },
    }).catch(() => {});
  }

  console.log('leaked listeners:', source.token._listeners.length);
  // v1.15.1: 500 (about 550 MB retained after GC)
  // with this fix: 0
})();

The fix just adds a done() call to each of the four error handlers. The success path and the http/fetch adapters already do this.


Summary by cubic

Fixes a memory leak in the axios XHR adapter by calling done() on error, timeout, abort, and cancel paths to unsubscribe cancelToken/AbortSignal listeners. Prevents listener buildup when reusing tokens or controllers.

Description

  • Summary of changes: Call done() in onabort, onerror, ontimeout, and onCanceled to remove listeners; aligns with success path and http/fetch adapters.
  • Reasoning: Failed requests previously rejected without detaching listeners, retaining config/request references and growing memory.
  • Additional context: No behavior change to cancellation; this is cleanup on failure paths only.

Docs

  • Update /docs/ cancellation guide to note XHR now auto-cleans listeners on error/abort/timeout and that reusing tokens/controllers no longer leaks listeners.

Testing

  • Added browser tests verifying:
    • cancelToken listener is removed after network error, timeout, and browser abort.
    • AbortSignal listener count returns to zero after a network error.
  • No additional tests needed.

Semantic version impact

  • Patch.

Written for commit 8653298. Summary will update on new commits.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

@jasonsaayman jasonsaayman merged commit 640458d into axios:v1.x Apr 25, 2026
23 checks passed
ModyQyW added a commit to uni-helper/uni-network that referenced this pull request May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants