Visitar URL original
fix(xhr): reject navigation-canceled requests as ECONNABORTED by ari-token-security · Pull Request #11094 · axios/axios · GitHub
Skip to content

fix(xhr): reject navigation-canceled requests as ECONNABORTED - #11094

Merged
jasonsaayman merged 8 commits into
axios:v1.xfrom
ari-token-security:fix/xhr-status-0-navigation-cancel
Jul 30, 2026
Merged

jasonsaayman merged 8 commits into
axios:v1.xfrom
ari-token-security:fix/xhr-status-0-navigation-cancel

Conversation

@ari-token-security

@ari-token-security ari-token-security commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Firefox 152 stopped firing error and abort for XHRs canceled by a document navigation, emitting only readystatechange and loadend (bugzilla 1505389, and Mozilla upstreamed a conformance test for it in wpt#59603). Since onerror/onabort are what reject and null the request handle, and that null is the only thing making onloadend bail, onloadend became the only handler that runs. settle() then resolved the canceled request through its !response.status branch, so callers received a successful response carrying status: 0 and an empty body.

This rejects that case in the XHR adapter instead, with ECONNABORTED / Request aborted. That is the same error Firefox 151 produced for the identical cancellation, so an application sees no change in outcome across the Firefox update. Mozilla confirmed the old behavior on the bug ("only the onabort callback is called instead of onerror") and the patch that changed it is titled "Fires readystatechange and loadend intead of abort".

lib/core/settle.js is deliberately untouched, because it is shared with the fetch adapter where an opaque no-cors response legitimately carries status 0.

Patch-level behavior change, not a breaking API change. Browser requests whose response reaches loadend with status 0 previously resolved with an empty body and now reject with ECONNABORTED / Request aborted. Two consequences worth stating explicitly:

  • Reads over file:, which some environments report as status 0 on success, still resolve. The exemption looks at the scheme of the request URL, at the scheme of the page origin for relative URLs (which inherit it), and at responseURL where implemented.
  • The rejection is not suppressed by validateStatus. It never consulted it for status 0 anyway, since !response.status short-circuits ahead of it, and onerror/onabort reject unconditionally for the same reason: there is no response to validate.

This is XHR-only. The fetch and Node HTTP adapters do not have the equivalent failure mode, and are unchanged.

Linked issue

Closes #11093

Changes

  • lib/adapters/xhr.js: reject with ECONNABORTED when onloadend is reached with status 0, excluding file: reads.
  • tests/browser/requests.browser.test.js: a rejection test for the non-file: case, and a resolve test for a relative file: read, placed next to the existing file-protocol test.
  • tests/unit/adapters/xhr.test.js: unit tests that drive the real adapter in Node with stubbed globals, covering the relative file: read without responseURL raised in review, plus reject and resolve controls for the other exemption sources.
  • PRE_RELEASE_CHANGELOG.md: entry under Bug Fixes.

Checklist

  • Tests added or updated (or N/A with reason)
  • Docs/types updated if public API changed (index.d.ts and index.d.cts) - N/A, no public API change. The existing ECONNABORTED description ("timed out ... or aborted by the browser or its plugin") already covers this case.
  • No breaking changes (or called out explicitly above)

Validation

Run against this branch, on top of v1.x at 5097193 (1.19.0):

npm run lint                            exit 0
npm run build                           exit 0
npm run test:vitest:browser:headless    591 passed (chromium, firefox, webkit)

npm run test:vitest:unit has 2 failures in tests/unit/adapters/fetch.test.js, both of which reproduce identically on a pristine checkout of v1.x, so they are unrelated to this change.

Each new assertion was mutation-tested: removing the guard fails the rejection tests, and dropping any of the three file: exemption sources fails a corresponding test.

Note that Playwright's bundled Firefox is 151, which predates the behavior being fixed, so the regression cannot be reproduced through the browser runner. The tests use the existing MockXMLHttpRequest, whose finish() already fires only onloadend, which is exactly the Firefox 152 shape. The browser harness also cannot serve a page over file:, so the page-origin exemption is covered by the unit tests instead, which stub the globals before importing axios so the real adapter runs with a file: origin.

🏄 Investigated and written with AI assistance, reviewed before opening.


Summary by cubic

Reject navigation-canceled XHRs with status 0 as ECONNABORTED to prevent empty successes on Firefox 152+. Successful file: reads still resolve, including relative paths inferred from the page’s file: origin or via responseURL.

  • Description

    • XHR adapter now rejects on loadend with status 0 unless it’s a file: read (checks normalized request URL, page origin for relative URLs, and responseURL).
    • Added normalizeURLForProtocolCheck to match WHATWG URL preprocessing; used by XHR protocol checks and imported by buildFullPath.
    • Leaves lib/core/settle.js unchanged to avoid impacting fetch’s opaque no-cors responses.
    • Rejection occurs before validateStatus, since no response was received.
  • Testing and Impact

    • Browser tests: reject non-file: status 0; resolve absolute/relative file: reads, including via responseURL.
    • Unit tests: cover page-origin inference, URL normalization (leading spaces/C0 controls), and that validateStatus is not called.
    • Docs: update /docs/ browser/XHR section to note status 0 on loadend rejects as ECONNABORTED, except successful file: reads; clarify validateStatus is not applied without a response.
    • Semantic version: Patch (XHR-only bug fix; Node and fetch adapters unchanged).

Written for commit 27b9d97. Summary will update on new commits.

Review in cubic

onerror and onabort are what reject a request that never received a
response, and they null the request handle, which is the only thing
that makes onloadend bail. Firefox 152 stopped firing either of them
for requests canceled by a document navigation, emitting only
readystatechange and loadend, so onloadend became the only handler
that runs and settle() resolved the request through its
!response.status branch. Callers got a successful response carrying
status 0 and an empty body, which then fails any JSON parsing or
schema validation downstream.

Firefox 151 raised abort for the same cancellation, which this adapter
reports as ECONNABORTED, so rejecting with that code leaves the
outcome an application sees unchanged across the update. Mozilla
confirmed the old behavior on the bug, and the patch that changed it
is titled "Fires readystatechange and loadend intead of abort".

The file: exception is keyed on both the request URL and responseURL.
A page loaded from file: may request a relative path that carries no
scheme, and responseURL is unavailable in some of the environments
where a successful file: read is reported as status 0.

Ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1505389

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread lib/adapters/xhr.js Outdated
@greptile-apps

greptile-apps Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

Greptile Summary

Rejects status-zero XHR completions as aborted while preserving successful file: reads.

  • Normalizes request URLs before protocol detection so browser-trimmed network URLs are not mistaken for relative file: URLs.
  • Retains exemptions based on the request URL, page origin, or XHR responseURL.
  • Adds browser and unit coverage for rejection, file-origin inheritance, explicit network URLs, and validateStatus behavior.
  • Documents the behavior change in the prerelease changelog.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failures remain.

T-Rex T-Rex Logs

What T-Rex did

  • The unit tests for the xhr adapter were executed and completed with exit code 0.
  • The verbose test run verified that relative and absolute file resolution checks for tests/unit/adapters/xhr.test.js passed, and it confirmed the non-file status-0 rejection and that the test asserts rejection before validateStatus can accept status 0.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
lib/adapters/xhr.js Adds status-zero rejection and correctly applies browser-style URL preprocessing before the file-protocol exemption.
lib/helpers/normalizeURLForProtocolCheck.js Extracts reusable WHATWG-style preprocessing for protocol checks.
lib/core/buildFullPath.js Replaces the local URL-normalization implementation with the shared helper without changing behavior.
tests/browser/requests.browser.test.js Adds browser-level coverage for status-zero rejection and responseURL-based file reads.
tests/unit/adapters/xhr.test.js Covers file-origin fallback, explicit network URLs with browser-trimmed prefixes, and unconditional aborted-request rejection.

Reviews (5): Last reviewed commit: "fix(xhr): normalize URL before status 0 ..." | Re-trigger Greptile

Comment thread lib/adapters/xhr.js Outdated
A relative request URL carries no scheme, so a read from a page served
over file: was only exempted from the status-0 rejection when the
engine exposed responseURL. Fall back to the scheme of platform.origin,
which a relative URL inherits, so such reads keep resolving when
responseURL is unavailable.

Unit tests drive the real adapter with stubbed globals to pin all
three exemption sources and both rejection paths.
…gation-cancel

# Conflicts:
#	PRE_RELEASE_CHANGELOG.md
@jasonsaayman jasonsaayman added the commit::fix The PR is related to a bugfix label Jul 29, 2026
Comment thread lib/adapters/xhr.js Outdated
@jasonsaayman
jasonsaayman merged commit c3f553c into axios:v1.x Jul 30, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commit::fix The PR is related to a bugfix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XHR adapter resolves navigation-canceled requests as success on Firefox 152+ (status 0, empty body)

2 participants