Repository navigation
Description
Summary
FormData.append() stringifies every value, so a File or Blob is stored
as the string "[object Object]". Consequently a Request/fetch with a
FormData body sends zero bytes and no content-type.
Per the spec, append(name, value) stringifies only when value is not a
Blob; a Blob/File must be kept as an entry value.
This is the outgoing half of multipart. The incoming half is fine — the
Request.formData() fix from #9617 works correctly, verified separately below.
Environment
| Perry | d36a1af0c (2026-09-05), reports 0.5.1520 |
| Platform | Linux x86_64 |
Repro
const bytes = new Uint8Array(1024);
const file = new File([bytes], "rad.jpg", { type: "image/jpeg" });
const blob = new Blob([bytes], { type: "application/octet-stream" });
const fd = new FormData();
fd.append("f", file);
fd.append("b", blob);
fd.append("t", "hello");
console.log(fd.get("f") instanceof File); // expected true
const req = new Request("http://x/", { method: "POST", body: fd });
console.log(req.headers.get("content-type")); // expected multipart/form-data; boundary=…
console.log((await req.arrayBuffer()).byteLength); // expected > 2048Observed:
File before append typeof=object isFile=true isBlob=true size=1024 name=rad.jpg
Blob before append typeof=object isFile=false isBlob=true size=1024
fd.get('f') [File] typeof=string ctor=String isFile=false isBlob=false str="[object Object]"
fd.get('b') [Blob] typeof=string ctor=String isFile=false isBlob=false str="[object Object]"
fd.get('t') [text] typeof=string ctor=String str="hello"
keys=["f","b","t"]
Request content-type: null
Request body bytes: 0 (expect > 2048)
So:
File/Blobconstruct correctly —size,name,typeandinstanceof
are all right beforeappend.appendstores them as"[object Object]".new Request(…, { body: fd })sets nocontent-typeand yields a
0-byte body.fetchdoes the same, so the server sees an empty request.
Note constructor.name is null for a correctly-constructed File/Blob,
which may or may not be related.
The incoming side is fine
Hand-crafting a multipart body and parsing it on the server works exactly as
expected on this same build — so #9617's fix is good and this is a separate
defect:
formDataKeys: ["file","caption"]
file → isFile=true isBlob=true name="a.bin" size=9 mime="application/octet-stream"
caption → typeof=string "hello"
Impact
Any client that uploads with FormData sends an empty body. It fails
quietly — the request is accepted, the server simply sees no file. In our
case (MB24) that is listing photos, KYC documents and claim photos.
It also makes a FormData-based test unable to exercise a working server
parser, which is what masked the state of #9617 for us: our spike posts with
new FormData(), so it still failed after the parser was fixed.
Activity
proggeramlug commentedon Sep 6, 2026
Fixed by #9868, landed on
mainvia merge train #9883. The train merged as its own branch, so the close-keyword never fired — closing manually, verified on main.