denoland/deno · error · TypeError

Multipart part has too many headers

Error message

Multipart part has too many headers

What it means

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().

Solutions

  1. If you control the client, fix the generator so each part carries only Content-Disposition (and Content-Type for files).
  2. On public servers, cap the accepted body size before parsing and return 413, so this parser limit is rarely reached.
  3. Treat this error as a 400 Bad Request: wrap formData() in try/catch and respond with 400 for TypeError from the parser.

Example fix

// before
const form = await request.formData(); // may throw on hostile input

// after
let form;
try {
  form = await request.formData();
} catch (e) {
  if (e instanceof TypeError) return new Response("malformed form data", { status: 400 });
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

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; }

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/070a6b88b76386be. Report an issue: GitHub.

Appendix: source

Thrown at ext/fetch/21_formdata.js:436

  }

  /**
   * @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];
      const sepIndex = StringPrototypeIndexOf(rawHeader, ":");
      if (sepIndex < 0) {
        continue; // Skip this header
      }
      headerCount++;
      if (headerCount > MAX_MULTIPART_PART_HEADER_COUNT) {
        throw new TypeError("Multipart part has too many headers");
      }
      const key = StringPrototypeSlice(rawHeader, 0, sepIndex);
      const value = StringPrototypeSlice(rawHeader, sepIndex + 1);
      headers.set(key, value);
    }

    const disposition = parseContentDisposition(
      headers.get("Content-Disposition") ?? "",
    );

    return { headers, disposition };
  }

  /**
   * @param {number} index
   * @returns {boolean}
   */
  #matchesBoundary(index) {

View on GitHub (pinned to 9ad36f7a2c)