denoland/deno · error · TypeError
Cannot construct MultipartParser: multipart/form-data must…
Error message
Cannot construct MultipartParser: multipart/form-data must provide a boundary
What it means
Thrown by MultipartParser's constructor (ext/fetch/21_formdata.js) when the boundary argument is falsy. The parser is created by request.formData()/response.formData() when the Content-Type is multipart/form-data; if no boundary parameter was supplied, there is no way to split parts, so parsing aborts immediately.
Solutions
- Send `Content-Type: multipart/form-data; boundary=----formdata-deno` (any token boundary) and use that same boundary in the body delimiters.
- Better: let the client library generate the body — `const fd = new FormData(); fetch(url, { method: "POST", body: fd })` sets the boundary automatically.
- In servers, validate the content type before calling formData() and reply 400 with a clear message when boundary is missing.
Example fix
// before (manual request)
await fetch(url, {
method: "POST",
headers: { "content-type": "multipart/form-data" }, // no boundary
body: handMadeBody,
});
// after
const fd = new FormData();
fd.set("file", new Blob([bytes]), "a.bin");
await fetch(url, { method: "POST", body: fd }); // boundary auto-set Defensive patterns
Strategy: validation
Validate before calling
function hasMultipartBoundary(req) {
const ct = req.headers.get("content-type") ?? "";
return /multipart\/form-data/i.test(ct) && /boundary=/i.test(ct);
}
if (!hasMultipartBoundary(req)) return new Response("multipart boundary required", { status: 400 });
const form = await req.formData(); Type guard
function isMultipartWithBoundary contentTypeHeader(req: Request): boolean {
const ct = req.headers.get("content-type")?.toLowerCase() ?? "";
return ct.startsWith("multipart/form-data") && ct.includes("boundary=");
} Try / catch
try { await req.formData(); } catch (e) { if (e instanceof TypeError && e.message.includes("boundary")) return new Response("bad content type", { status: 400 }); throw e; } Prevention
- Always send form data with new FormData() so the runtime generates the boundary.
- Validate content-type on the server before calling formData().
- Watch for proxies that rewrite content-type and drop parameters.
When it happens
Trigger: `await req.formData()` where Content-Type is exactly `multipart/form-data` (no `; boundary=...`). Also calling new MultipartParser(body) directly without a boundary in internal code.
Common situations: Hand-crafted multipart requests (curl --data with a manually set content type); a gateway/proxy rewriting or stripping the content type; client code that copies a hardcoded content-type string and forgets the boundary; test fixtures built by hand.
Related errors
- Cannot turn into form data: missing boundary parameter in…
- Body can not be decoded as form data
- Missing content type
- Unable to parse body as form data
- Invalid WebAssembly content type
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/5106d8e463cfb3c3.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/21_formdata.js:411
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.body = body;
this.boundaryChars = core.encode(`--${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) {
const rawHeader = rawHeaders[i];View on GitHub (pinned to 9ad36f7a2c)