Skip to content

fix: remove forced multipart header to fix Android postForm/patchForm…#7243

Open
neel3o115 wants to merge 1 commit intoaxios:v1.xfrom
neel3o115:fix/rn-android-postform
Open

fix: remove forced multipart header to fix Android postForm/patchForm…#7243
neel3o115 wants to merge 1 commit intoaxios:v1.xfrom
neel3o115:fix/rn-android-postform

Conversation

@neel3o115
Copy link
Copy Markdown

@neel3o115 neel3o115 commented Nov 18, 2025

This PR fixes a long-standing issue where axios.postForm, axios.putForm, and axios.patchForm fail on React Native Android with a Network Error.
The issue occurs because Axios forces a bare:
Content-Type: multipart/form-data
header without a boundary.
React Native Android requires a boundary (e.g. boundary=----RNFormBoundaryABC123).
When Axios overrides the header, React Native cannot append the boundary, causing the request to be malformed and immediately rejected.

iOS is more permissive, so it does not fail.

What This PR Changes:-

  1. Removes the forced 'Content-Type': 'multipart/form-data' header
  2. Preserves user-provided headers
  3. Does not affect existing behavior on:

iOS React Native (already worked)

Standard axios.post / axios.patch / axios.put (not using FormData helpers)

Technical Summary

Before (broken on Android):
headers: { 'Content-Type': 'multipart/form-data' }

After (correct):
headers: isForm ? (config?.headers || {}) : {}

This lets React Native generate:
Content-Type: multipart/form-data; boundary=----RNGeneratedBoundary123
which Android accepts.

Result:-

  • Android multipart uploads work
  • No more Network Error
  • No breaking changes
  • Fully backward-compatible

Fixes
#6968

@nidhishgajjar

This comment was marked as spam.

1 similar comment
@nidhishgajjar

This comment was marked as spam.

@jasonsaayman jasonsaayman added type::breaking The PR introduces breaking changes commit::fix The PR is related to a bugfix labels Apr 28, 2026
@jasonsaayman jasonsaayman added priority::medium A medium priority status::changes-requested A reviewer requested changes to the PR status::needs-rebase The PR requires a rebase and removed type::breaking The PR introduces breaking changes labels Apr 29, 2026
Copy link
Copy Markdown
Member

@jasonsaayman jasonsaayman left a comment

Choose a reason for hiding this comment

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

Thanks @neel3o115, and apologies on the wait. The diagnosis is right, Android's RN networking layer rejects bare multipart/form-data without a boundary, iOS doesn't, and the gap exists because RN is neither hasStandardBrowserEnv (axios deliberately excludes RN from that flag in resolveConfig) nor uses Node's form-data package. The placeholder header reaches the wire untouched on RN, and Android complains. Worth fixing.

Three asks before this can merge.

First, drop the toFormData.js changes. Function('return this')() is eval in a different shape — it trips CSP without unsafe-eval, which covers most production browsers behind a CSP and several RN setups. globalThis is universally available everywhere axios runs if global access were ever needed, but it isn't here. The precedence flip from PlatformFormData || FormData to runtimeFD || PlatformFormData is also a silent breakage on 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() (documented form-data API) gets an object without them. And 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 is typeof FormData !== 'undefined' ? FormData : null. In RN, that's the runtime's FormData.

Second, tighten the Axios.js change. What you have:

  headers: isForm
    ? (config && config.headers ? config.headers : {})
    : {},

config.headers is already merged in by mergeConfig(config || {}, …) — the second argument is the overlay, not the source. Re-passing config.headers is either a no-op or runs the header merge strategy twice. The minimal version is headers: {}, or drop the headers key from the overlay entirely. Same effect: no placeholder is forced, anything in config.headers still flows through normally.

Third, add tests. At minimum: postForm (and put/patch) no longer emit the bare 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.

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 priority::medium A medium priority status::changes-requested A reviewer requested changes to the PR status::needs-rebase The PR requires a rebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants