{"record":{"id":"1ff4f6fe291b8de7","repo":"remix-run/remix","slug":"invalid-multipart-stream-missing-initial-boundary","errorCode":null,"errorMessage":"Invalid multipart stream: missing initial boundary","messagePattern":"Invalid multipart stream: missing initial boundary","errorType":"exception","errorClass":"MultipartParseError","httpStatus":null,"severity":"error","filePath":"packages/multipart-parser/src/lib/multipart.ts","lineNumber":430,"sourceCode":"        this.#currentHeader = chunk.subarray(index, headerEndIndex)\n        this.#currentContent = []\n        this.#contentLength = 0\n\n        index = headerEndIndex + 4 // Skip header + \\r\\n\\r\\n\n\n        this.#state = MultipartParserStateBody\n\n        continue\n      }\n\n      if (this.#state === MultipartParserStateStart) {\n        if (chunkLength < this.#openingBoundaryLength) {\n          this.#buffer = chunk\n          break\n        }\n\n        if (this.#findOpeningBoundary(chunk) !== 0) {\n          throw new MultipartParseError('Invalid multipart stream: missing initial boundary')\n        }\n\n        index = this.#openingBoundaryLength\n\n        this.#state = MultipartParserStateAfterBoundary\n      }\n    }\n  }\n\n  #append(chunk: Uint8Array): void {\n    if (chunk.length === 0) {\n      return\n    }\n\n    if (this.#contentLength + chunk.length > this.maxFileSize) {\n      throw new MaxFileSizeExceededError(this.maxFileSize)\n    }\n","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/multipart-parser/src/lib/multipart.ts#L412-L448","documentation":"The first bytes of a multipart body must be the opening delimiter (--boundary). If the parser's first chunk doesn't begin with the expected opening boundary, it throws MultipartParseError('Invalid multipart stream: missing initial boundary'), because nothing in the body can be reliably located otherwise.","triggerScenarios":"A body that starts with a preamble or garbage instead of --boundary; a boundary extracted from the Content-Type that doesn't match the actual body delimiters (e.g. extra quotes, case differences, or URL-encoding); truncated or restarted uploads.","commonSituations":"Content-Type boundary value not matching the body (proxy mangling, manual header construction), clients sending a nonstandard preamble, or a partially-consumed body stream being reparsed.","solutions":["Verify the boundary in the Content-Type exactly matches the --boundary delimiters in the raw body (dump the first ~100 bytes)","Ensure the Content-Type header is generated by the same FormData/client that built the body — never mix manual headers with generated bodies","Remove any preamble before the first --boundary if you control the sender"],"exampleFix":"// before\nheaders: { 'Content-Type': `multipart/form-data; boundary=${myBoundary}` }\nbody: `--${otherBoundary}\\r\\n...` // mismatch throws\n\n// after\nlet body = `--${myBoundary}\\r\\nContent-Disposition: form-data; name=\"f\"\\r\\n\\r\\nv\\r\\n--${myBoundary}--\\r\\n`\nheaders: { 'Content-Type': `multipart/form-data; boundary=${myBoundary}` }","handlingStrategy":"try-catch","validationCode":"// sanity-check the first bytes before parsing (when body is buffered)\nlet head = new TextDecoder().decode(bodyBytes.subarray(0, boundary.length + 4))\nif (!head.startsWith(`--${boundary}`)) {\n  throw new Response('Body does not start with multipart boundary', { status: 400 })\n}","typeGuard":null,"tryCatchPattern":"try {\n  for await (let part of parseMultipartStream(stream, { boundary })) { /* ... */ }\n} catch (error) {\n  if (error instanceof MultipartParseError && error.message.includes('missing initial boundary')) {\n    return new Response('Malformed multipart body', { status: 400 })\n  }\n  throw error\n}","preventionTips":["Generate Content-Type and body from the same FormData object","Dump the first bytes of failing bodies to spot boundary mismatches fast"],"tags":["multipart","boundary","stream-malformed"],"backgroundTag":"multipart-stream-malformed","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}