Tracking issue for a maintainer to pick up. Splits the legitimate fix out of #7243 so the actual bug from #6968 can land cleanly.
Background
axios.postForm / putForm / patchForm in lib/core/Axios.js currently force a placeholder header on the overlay config:
headers: isForm ? { 'Content-Type': 'multipart/form-data' } : {}
The intent is to let the adapter rewrite this with a real boundary later. That works on:
- The browser XHR adapter (the platform appends the boundary).
- The Node HTTP adapter via the
form-data package (getHeaders() overwrites it).
- The fetch adapter / spec-compliant
FormData via formDataToStream (boundary attached).
It does not work on React Native Android. RN is neither hasStandardBrowserEnv (axios deliberately excludes RN in resolveConfig) nor does it use form-data. The bare placeholder reaches the wire untouched, and Android's networking layer rejects multipart/form-data without a boundary. iOS is more permissive, which is why this only reproduces on Android.
Original report: #6968. Attempted fix: #7243 (closed — see review for why that diff isn't the right shape).
Proposed fix
In lib/core/Axios.js, in the post/put/patch helper factory, drop the placeholder entirely:
return this.request(mergeConfig(config || {}, {
method,
url,
data
}));
(Or headers: {} if we'd rather be explicit.) config.headers is already merged in by mergeConfig(config || {}, …) — the second argument is the overlay, not the source — so anything the caller passes still flows through. No placeholder, nothing to overwrite, no Android breakage.
The toFormData.js changes in #7243 should not be carried over:
Function('return this')() is eval in a different shape; trips CSP without unsafe-eval (most production browsers behind a CSP, several RN setups).
- The precedence flip from
PlatformFormData || FormData to runtimeFD || PlatformFormData silently breaks Node: Node 18+ has global FormData, so this picks the spec-compliant one over form-data, and any caller of axios.toFormData(obj) on Node who relied on .getHeaders() / .getLength() gets an object without them.
- It isn't needed for the RN bug — RN bundlers resolve the
browser field in package.json, so PlatformFormData already points at lib/platform/browser/classes/FormData.js, which resolves to the RN runtime's FormData.
Tests required
postForm / putForm / patchForm no longer emit a bare Content-Type: multipart/form-data placeholder.
- Node
form-data path still ends up with Content-Type: multipart/form-data; boundary=… via getHeaders().
- Spec-compliant
FormData path through the http adapter still gets a boundary via formDataToStream.
- Caller-provided
Content-Type on the request config is preserved through the FormData helpers.
Acceptance
Tracking issue for a maintainer to pick up. Splits the legitimate fix out of #7243 so the actual bug from #6968 can land cleanly.
Background
axios.postForm/putForm/patchForminlib/core/Axios.jscurrently force a placeholder header on the overlay config:The intent is to let the adapter rewrite this with a real boundary later. That works on:
form-datapackage (getHeaders()overwrites it).FormDataviaformDataToStream(boundary attached).It does not work on React Native Android. RN is neither
hasStandardBrowserEnv(axios deliberately excludes RN inresolveConfig) nor does it useform-data. The bare placeholder reaches the wire untouched, and Android's networking layer rejectsmultipart/form-datawithout a boundary. iOS is more permissive, which is why this only reproduces on Android.Original report: #6968. Attempted fix: #7243 (closed — see review for why that diff isn't the right shape).
Proposed fix
In
lib/core/Axios.js, in thepost/put/patchhelper factory, drop the placeholder entirely:(Or
headers: {}if we'd rather be explicit.)config.headersis already merged in bymergeConfig(config || {}, …)— the second argument is the overlay, not the source — so anything the caller passes still flows through. No placeholder, nothing to overwrite, no Android breakage.The
toFormData.jschanges in #7243 should not be carried over:Function('return this')()isevalin a different shape; trips CSP withoutunsafe-eval(most production browsers behind a CSP, several RN setups).PlatformFormData || FormDatatoruntimeFD || PlatformFormDatasilently breaks Node: Node 18+ has globalFormData, so this picks the spec-compliant one overform-data, and any caller ofaxios.toFormData(obj)on Node who relied on.getHeaders()/.getLength()gets an object without them.browserfield inpackage.json, soPlatformFormDataalready points atlib/platform/browser/classes/FormData.js, which resolves to the RN runtime'sFormData.Tests required
postForm/putForm/patchFormno longer emit a bareContent-Type: multipart/form-dataplaceholder.form-datapath still ends up withContent-Type: multipart/form-data; boundary=…viagetHeaders().FormDatapath through the http adapter still gets a boundary viaformDataToStream.Content-Typeon the request config is preserved through the FormData helpers.Acceptance
lib/core/Axios.jsonly.tests/unit/covering the three paths above.