{"record":{"id":"5106d8e463cfb3c3","repo":"denoland/deno","slug":"cannot-construct-multipartparser-multipart-form-d","errorCode":null,"errorMessage":"Cannot construct MultipartParser: multipart/form-data must provide a boundary","messagePattern":"Cannot construct MultipartParser: multipart/form-data must provide a boundary","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/21_formdata.js","lineNumber":411,"sourceCode":"const MAX_MULTIPART_PART_HEADER_SIZE = 16 * 1024;\nconst MAX_MULTIPART_PART_HEADER_COUNT = 128;\n\n/**\n * @param {Uint8Array} bytes\n * @returns {string}\n */\nfunction decodeLatin1Bytes(bytes) {\n  return ReflectApply(StringFromCharCode, null, bytes);\n}\n\nclass MultipartParser {\n  /**\n   * @param {Uint8Array} body\n   * @param {string | undefined} boundary\n   */\n  constructor(body, boundary) {\n    if (!boundary) {\n      throw new TypeError(\n        \"Cannot construct MultipartParser: multipart/form-data must provide a boundary\",\n      );\n    }\n\n    this.body = body;\n    this.boundaryChars = core.encode(`--${boundary}`);\n  }\n\n  /**\n   * @param {string} headersText\n   * @returns {{ headers: Headers, disposition: Map<string, string> }}\n   */\n  #parseHeaders(headersText) {\n    const headers = new Headers();\n    const rawHeaders = StringPrototypeSplit(headersText, \"\\r\\n\");\n    let headerCount = 0;\n    for (let i = 0; i < rawHeaders.length; ++i) {\n      const rawHeader = rawHeaders[i];","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/fetch/21_formdata.js#L393-L429","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"// before (manual request)\nawait fetch(url, {\n  method: \"POST\",\n  headers: { \"content-type\": \"multipart/form-data\" }, // no boundary\n  body: handMadeBody,\n});\n\n// after\nconst fd = new FormData();\nfd.set(\"file\", new Blob([bytes]), \"a.bin\");\nawait fetch(url, { method: \"POST\", body: fd }); // boundary auto-set","handlingStrategy":"validation","validationCode":"function hasMultipartBoundary(req) {\n  const ct = req.headers.get(\"content-type\") ?? \"\";\n  return /multipart\\/form-data/i.test(ct) && /boundary=/i.test(ct);\n}\nif (!hasMultipartBoundary(req)) return new Response(\"multipart boundary required\", { status: 400 });\nconst form = await req.formData();","typeGuard":"function isMultipartWithBoundary contentTypeHeader(req: Request): boolean {\n  const ct = req.headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n  return ct.startsWith(\"multipart/form-data\") && ct.includes(\"boundary=\");\n}","tryCatchPattern":"try { await req.formData(); } catch (e) { if (e instanceof TypeError && e.message.includes(\"boundary\")) return new Response(\"bad content type\", { status: 400 }); throw e; }","preventionTips":["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."],"tags":["form-data","multipart","fetch","content-type"],"backgroundTag":"missing-multipart-boundary","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}