When passing a native (global) FormData instance as the body to undici's fetch(), the request body is sent as the literal string [object FormData] with Content-Type: text/plain instead of a multipart/form-data body. This regression was introduced in v8.0.2.
Reproducible By
Use Node.js 22 or later (which exposes FormData as a global)
Create a FormData using the native global — not undici's own FormData
Pass it as the body to undici's fetch()
import { fetch } from 'undici';
const form = new FormData(); // native globalThis.FormData
form.append('field', 'value');
const res = await fetch('https://httpbin.org/post', { method: 'POST', body: form });
const data = await res.json();
console.log(data.form); // {} - empty, should be { field: 'value' }
console.log(data.data); // '[object FormData]' — body was stringified
Root cause
XMLHttpRequestBodyInit in lib/web/fetch/response.js checks webidl.is.FormData(V), which only matches undici’s internal FormData class.
Native globalThis.FormData fails this check and falls through to webidl.converters.DOMString(V), which calls .toString() on the object.
The same issue exists in extractBody in lib/web/fetch/body.js.
Fix
Add a fallback instanceof check in both places:
if (webidl.is.FormData(V) || (typeof globalThis.FormData !== 'undefined' && V instanceof globalThis.FormData)) {
return V;
}
Expected Behavior
The request is sent as multipart/form-data with the form fields correctly encoded.
Logs & Screenshots
// data.data
'[object FormData]'
// data.headers['Content-Type']
'text/plain; charset=UTF-8'
Environment
undici 8.0.2
Node.js v24.14.0
Linux
Additional context
This worked correctly before v8.0.2. The issue surfaces silently — no error is thrown, the request succeeds with a 200, but the server receives a meaningless body.
Particularly hard to debug when the downstream API returns a domain-specific error (e.g. OpenAI returns "you must provide a model parameter") that gives no hint about the malformed body.
When passing a native (global) FormData instance as the body to undici's fetch(), the request body is sent as the literal string [object FormData] with Content-Type: text/plain instead of a multipart/form-data body. This regression was introduced in v8.0.2.
Reproducible By
Use Node.js 22 or later (which exposes FormData as a global)
Create a FormData using the native global — not undici's own FormData
Pass it as the body to undici's fetch()
import { fetch } from 'undici';
const form = new FormData(); // native globalThis.FormData
form.append('field', 'value');
const res = await fetch('https://httpbin.org/post', { method: 'POST', body: form });
const data = await res.json();
console.log(data.form); // {} - empty, should be { field: 'value' }
console.log(data.data); // '[object FormData]' — body was stringified
Root cause
XMLHttpRequestBodyInit in lib/web/fetch/response.js checks webidl.is.FormData(V), which only matches undici’s internal FormData class.
Native globalThis.FormData fails this check and falls through to webidl.converters.DOMString(V), which calls .toString() on the object.
The same issue exists in extractBody in lib/web/fetch/body.js.
Fix
Add a fallback instanceof check in both places:
if (webidl.is.FormData(V) || (typeof globalThis.FormData !== 'undefined' && V instanceof globalThis.FormData)) {
return V;
}
Expected Behavior
The request is sent as multipart/form-data with the form fields correctly encoded.
Logs & Screenshots
// data.data
'[object FormData]'
// data.headers['Content-Type']
'text/plain; charset=UTF-8'
Environment
undici 8.0.2
Node.js v24.14.0
Linux
Additional context
This worked correctly before v8.0.2. The issue surfaces silently — no error is thrown, the request succeeds with a 200, but the server receives a meaningless body.
Particularly hard to debug when the downstream API returns a domain-specific error (e.g. OpenAI returns "you must provide a model parameter") that gives no hint about the malformed body.