{"record":{"id":"6b582448fb3e8b95","repo":"remix-run/remix","slug":"exceeded-maximum-number-of-parts-6b5824","errorCode":null,"errorMessage":"Exceeded maximum number of parts","messagePattern":"Exceeded maximum number of parts","errorType":"exception","errorClass":"MaxPartsExceededError","httpStatus":null,"severity":"error","filePath":"packages/multipart-parser/src/lib/multipart.ts","lineNumber":460,"sourceCode":"      return\n    }\n\n    if (this.#contentLength + chunk.length > this.maxFileSize) {\n      throw new MaxFileSizeExceededError(this.maxFileSize)\n    }\n\n    if (this.#totalContentLength + chunk.length > this.maxTotalSize) {\n      throw new MaxTotalSizeExceededError(this.maxTotalSize)\n    }\n\n    this.#currentContent!.push(chunk)\n    this.#contentLength += chunk.length\n    this.#totalContentLength += chunk.length\n  }\n\n  #createPart(): MultipartPart {\n    if (++this.#partCount > this.maxParts) {\n      throw new MaxPartsExceededError(this.maxParts)\n    }\n\n    return new MultipartPart(this.#currentHeader!, this.#currentContent!)\n  }\n\n  #analyzeCarryBoundary(\n    carry: Uint8Array,\n    chunk: Uint8Array,\n  ): { kind: 'none' } | { kind: 'partial'; start: number } | { kind: 'full'; start: number } {\n    let totalLength = carry.length + chunk.length\n\n    for (let start = 0; start < carry.length; ++start) {\n      let availableLength = totalLength - start\n      let compareLength = Math.min(this.#boundaryLength, availableLength)\n\n      let matched = true\n      for (let i = 0; i < compareLength; ++i) {\n        let sourceIndex = start + i","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/multipart-parser/src/lib/multipart.ts#L442-L478","documentation":"Each time the parser starts a new part, #createPart increments a counter and throws MaxPartsExceededError when the count surpasses maxParts. This bounds how many parts a single request may contain, preventing part-count floods that would otherwise fit under size limits.","triggerScenarios":"A multipart message with more parts than maxParts — many small form fields or many tiny files in one request.","commonSituations":"Clients appending hundreds/thousands of parts (e.g. per-chunk metadata), abuse via part flooding, or low default limits meeting legitimate multi-file forms.","solutions":["Raise maxParts in MultipartParserOptions if the form legitimately has many fields/files","Reduce the number of parts the client sends (batch values into single fields)","Catch MaxPartsExceededError and respond 413/400"],"exampleFix":"// before\nparseMultipartRequest(request)\n\n// after\nparseMultipartRequest(request, { maxParts: 1000 })","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  for await (let part of parseMultipartRequest(request, { maxParts: PART_LIMIT })) { /* ... */ }\n} catch (error) {\n  if (error instanceof MaxPartsExceededError) {\n    return new Response('Too many parts', { status: 413 })\n  }\n  throw error\n}","preventionTips":["Set maxParts explicitly if forms have many fields","Avoid generating one part per item — batch values into fewer parts"],"tags":["multipart","limits","part-count"],"backgroundTag":"multipart-part-limit-exceeded","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}