denoland/deno · error · TypeError
Cannot construct MultipartParser: multipart/form-data must p
Error message
Cannot construct MultipartParser: multipart/form-data must provide a boundary
What it means
MultipartParser (internal class in ext/fetch/21_formdata.js) requires a truthy boundary string; its constructor throws immediately otherwise. The public route is request.formData()/response.formData(): 22_body.js extracts boundary from the Content-Type and only rejects a null (absent) parameter, so an empty boundary= value slips through as "" and fails here.
Source
Thrown at ext/fetch/21_formdata.js:409
const MAX_MULTIPART_PART_HEADER_SIZE = 16 * 1024;
const MAX_MULTIPART_PART_HEADER_COUNT = 128;
/**
* @param {Uint8Array} bytes
* @returns {string}
*/
function decodeLatin1Bytes(bytes) {
return ReflectApply(StringFromCharCode, null, bytes);
}
class MultipartParser {
/**
* @param {Uint8Array} body
* @param {string | undefined} boundary
*/
constructor(body, boundary) {
if (!boundary) {
throw new TypeError(
"Cannot construct MultipartParser: multipart/form-data must provide a boundary",
);
}
this.boundary = `--${boundary}`;
this.body = body;
this.boundaryChars = core.encode(this.boundary);
}
/**
* @param {string} headersText
* @returns {{ headers: Headers, disposition: Map<string, string> }}
*/
#parseHeaders(headersText) {
const headers = new Headers();
const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
let headerCount = 0;
for (let i = 0; i < rawHeaders.length; ++i) {View on GitHub (pinned to 89f33cbef2)
Solutions
- Fix the sending side to always generate a real boundary (browsers and the FormData + fetch path do this automatically)
- Validate before parsing: read the Content-Type, confirm the essence is multipart/form-data and the boundary parameter is present and non-empty
- Return 400 early when the boundary check fails instead of letting formData() throw
Example fix
// before
const form = await req.formData(); // throws on boundary=
// after
const ct = req.headers.get("content-type") ?? "";
const m = /boundary=([^;]+)/i.exec(ct);
if (!m || m[1].trim() === "") {
return new Response("bad multipart content-type", { status: 400 });
}
const form = await req.formData(); Defensive patterns
Strategy: validation
Validate before calling
const ct = req.headers.get("content-type") ?? "";
const boundary = /boundary=([^;]+)/i.exec(ct)?.[1]?.trim();
if (!boundary) return new Response("multipart boundary missing", { status: 400 });
const form = await req.formData(); Type guard
function hasMultipartBoundary(contentType: string | null): boolean {
return contentType != null && /multipart\/form-data/i.test(contentType) &&
/boundary=[^;]/i.test(contentType) && !/boundary=\s*(;|$)/i.test(contentType);
} Try / catch
try { return await req.formData(); } catch (e) {
if (e instanceof TypeError && e.message.includes("must provide a boundary")) {
return new Response("bad multipart body", { status: 400 });
}
throw e;
} Prevention
- Always generate the boundary on the sender; never hand-write boundary=
- Validate Content-Type parameters before parsing untrusted requests
- Return 400 for malformed multipart instead of letting the parser throw to the framework
When it happens
Trigger: A request/response with Content-Type: multipart/form-data; boundary= (empty parameter) reaching .formData(); hand-rolled multipart senders that emit the parameter with no value; malformed proxy rewrites that truncate the Content-Type.
Common situations: Custom test servers or PHP/Python scripts that build multipart Content-Types manually and forget the boundary value; proxies or middlewares that normalize and truncate header parameters; fuzzed input in security tests.
Related errors
- Cannot turn into form data: missing boundary parameter in mi
- Multipart part has too many headers
- Unable to parse body as form data
- Multipart part headers are too large
- Body can not be decoded as form data
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/5106d8e463cfb3c3.
Report an issue: GitHub.