denoland/deno · error · TypeError
Unable to parse body as form data
Error message
Unable to parse body as form data
What it means
MultipartParser.parseBody() rejects bodies that are too short to contain even two boundary delimiters: length must be >= boundary.length * 2 + 4. The only short bodies accepted are exactly the closing delimiter "--boundary--" (with optional trailing \r\n), i.e. an empty but valid form; everything else throws this TypeError.
Source
Thrown at ext/fetch/21_formdata.js:490
}
/**
* @returns {FormData}
*/
parse() {
// To have fields body must be at least 2 boundaries + \r\n + --
// on the last boundary.
if (this.body.length < (this.boundary.length * 2) + 4) {
const decodedBody = core.decode(this.body);
const lastBoundary = this.boundary + "--";
// check if it's an empty valid form data
if (
decodedBody === lastBoundary ||
decodedBody === lastBoundary + "\r\n"
) {
return new FormData();
}
throw new TypeError("Unable to parse body as form data");
}
const formData = new FormData();
let headerText = "";
let headerStart = 0;
let state = 0;
let fileStart = 0;
for (let i = 0; i < this.body.length; i++) {
const byte = this.body[i];
const prevByte = this.body[i - 1];
const isNewLine = byte === LF && prevByte === CR;
if (state === 0 && isNewLine) {
state = 1;
headerStart = i + 1;
} else if (
state === 1View on GitHub (pinned to 89f33cbef2)
Solutions
- Never pre-read or transform the body - call req.formData() exactly once on the original request
- Make the sender and receiver agree on the boundary: let the client generate it (FormData does) and the server parse it from Content-Type
- Check Content-Length vs actual body and abort/retry truncated uploads before parsing
Example fix
// before const text = await req.text(); const form = parseMultipartManually(text); // boundary/length drift -> this error path // after const form = await req.formData(); // parser sees the raw bytes and matching boundary
Defensive patterns
Strategy: try-catch
Validate before calling
// Sender/receiver sanity: boundary in Content-Type must match encoder's
const body = buildMultipart(fields, boundary);
const resp = await fetch(url, {
method: "POST",
headers: { "content-type": `multipart/form-data; boundary=${boundary}` },
body,
}); Try / catch
try { return await req.formData(); } catch (e) {
if (e instanceof TypeError && e.message.includes("Unable to parse body as form data")) {
return new Response("malformed multipart body", { status: 400 });
}
throw e;
} Prevention
- Never pre-read, decode, or re-encode req.body before formData()
- Use one boundary constant for building the body and the Content-Type
- Abort on short reads (Content-Length mismatch) instead of parsing truncated bodies
When it happens
Trigger: Calling .formData() on a truncated request body (client disconnected mid-upload); a Content-Type boundary= value that does not match the boundary actually used to encode the body; a body that was read/decoded once and re-encoded, changing its bytes.
Common situations: Proxy or middleware that consumes and reconstructs req.body before formData(); mismatched boundary between client library and hand-written server tests; uploads cut off by size limits or timeouts.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cannot construct MultipartParser: multipart/form-data must p
- Multipart part has too many headers
- Multipart part headers are too large
- Cannot turn into form data: missing boundary parameter in mi
- Body can not be decoded as form data
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/5daca81ee5011956.
Report an issue: GitHub.