{"record":{"id":"070a6b88b76386be","repo":"denoland/deno","slug":"multipart-part-has-too-many-headers","errorCode":null,"errorMessage":"Multipart part has too many headers","messagePattern":"Multipart part has too many headers","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/21_formdata.js","lineNumber":436,"sourceCode":"  }\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];\n      const sepIndex = StringPrototypeIndexOf(rawHeader, \":\");\n      if (sepIndex < 0) {\n        continue; // Skip this header\n      }\n      headerCount++;\n      if (headerCount > MAX_MULTIPART_PART_HEADER_COUNT) {\n        throw new TypeError(\"Multipart part has too many headers\");\n      }\n      const key = StringPrototypeSlice(rawHeader, 0, sepIndex);\n      const value = StringPrototypeSlice(rawHeader, sepIndex + 1);\n      headers.set(key, value);\n    }\n\n    const disposition = parseContentDisposition(\n      headers.get(\"Content-Disposition\") ?? \"\",\n    );\n\n    return { headers, disposition };\n  }\n\n  /**\n   * @param {number} index\n   * @returns {boolean}\n   */\n  #matchesBoundary(index) {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/fetch/21_formdata.js#L418-L454","documentation":"Thrown while parsing a single multipart part when the number of headers in that part exceeds MAX_MULTIPART_PART_HEADER_COUNT (128, ext/fetch/21_formdata.js:394). This is a resource-exhaustion guard (CVE-class hardening): a malicious part with thousands of header lines would otherwise burn CPU/memory in #parseHeaders().","triggerScenarios":"`await request.formData()` where one part's header block (lines containing ':') has more than 128 header lines. Legitimate browsers send 1-3 headers per part (Content-Disposition, Content-Type), so >128 indicates abuse or a broken generator.","commonSituations":"Fuzzed or hostile uploads against a public endpoint; a misbehaving client library that repeats the same header per line; denial-of-service attempts against formData() parsing.","solutions":["If you control the client, fix the generator so each part carries only Content-Disposition (and Content-Type for files).","On public servers, cap the accepted body size before parsing and return 413, so this parser limit is rarely reached.","Treat this error as a 400 Bad Request: wrap formData() in try/catch and respond with 400 for TypeError from the parser."],"exampleFix":"// before\nconst form = await request.formData(); // may throw on hostile input\n\n// after\nlet form;\ntry {\n  form = await request.formData();\n} catch (e) {\n  if (e instanceof TypeError) return new Response(\"malformed form data\", { status: 400 });\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { form = await request.formData(); } catch (e) { if (e instanceof TypeError && /too many headers|form data/i.test(e.message)) return new Response(\"malformed multipart\", { status: 400 }); throw e; }","preventionTips":["Treat multipart parsing of untrusted requests as fallible; always catch TypeError and map to 400.","Cap request body sizes at the server/proxy layer.","Keep part headers minimal (Content-Disposition, Content-Type only)."],"tags":["form-data","multipart","limits","dos-hardening"],"backgroundTag":"multipart-parse-limit-exceeded","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"}