{"record":{"id":"a6ce1dab32277331","repo":"remix-run/remix","slug":"invalid-content-type-header-missing-boundary","errorCode":null,"errorMessage":"Invalid Content-Type header: missing boundary","messagePattern":"Invalid Content-Type header: missing boundary","errorType":"validation","errorClass":"MultipartParseError","httpStatus":null,"severity":"error","filePath":"packages/multipart-parser/src/lib/multipart-request.ts","lineNumber":50,"sourceCode":" * @param options Optional parser options, such as `maxHeaderSize`, `maxFileSize`, `maxParts`,\n * and `maxTotalSize`\n * @yields Parsed {@link MultipartPart} objects from the request body\n * @returns An async generator yielding {@link MultipartPart} objects\n */\nexport async function* parseMultipartRequest(\n  request: Request,\n  options?: MultipartParserOptions,\n): AsyncGenerator<MultipartPart, void, unknown> {\n  if (!isMultipartRequest(request)) {\n    throw new MultipartParseError('Request is not a multipart request')\n  }\n  if (!request.body) {\n    throw new MultipartParseError('Request body is empty')\n  }\n\n  let boundary = getMultipartBoundary(request.headers.get('Content-Type')!)\n  if (!boundary) {\n    throw new MultipartParseError('Invalid Content-Type header: missing boundary')\n  }\n\n  yield* parseMultipartStream(request.body, {\n    boundary,\n    maxHeaderSize: options?.maxHeaderSize,\n    maxFileSize: options?.maxFileSize,\n    maxParts: options?.maxParts,\n    maxTotalSize: options?.maxTotalSize,\n  })\n}\n","sourceCodeStart":32,"sourceCodeEnd":61,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/multipart-parser/src/lib/multipart-request.ts#L32-L61","documentation":"The request's Content-Type is multipart, but the header lacks the boundary parameter required to delimit parts. Without a boundary the multipart body cannot be split, so the parser throws MultipartParseError immediately after extracting the header.","triggerScenarios":"Content-Type: multipart/form-data with no ; boundary=... parameter — typically from hand-built headers, some proxies/CDNs that rewrite the header, or manually constructed Requests where the content type was set without a boundary.","commonSituations":"Setting headers: { 'Content-Type': 'multipart/form-data' } manually in fetch (the most common mistake — omitting boundary); API gateways normalizing or truncating the Content-Type; copying content types from logs into test fixtures.","solutions":["Do not set the Content-Type manually when sending FormData — let fetch/the browser generate it with the boundary","If constructing manually, append a boundary: multipart/form-data; boundary=----formdata-xyz and use the same boundary in the body","Verify intermediate proxies/load balancers pass the full Content-Type through unchanged"],"exampleFix":"// before\nawait fetch(url, {\n  method: 'POST',\n  headers: { 'Content-Type': 'multipart/form-data' }, // no boundary\n  body: formData,\n})\n\n// after\nawait fetch(url, {\n  method: 'POST',\n  body: formData, // fetch sets Content-Type with boundary automatically\n})","handlingStrategy":"validation","validationCode":"let contentType = request.headers.get('Content-Type') ?? ''\nif (!/boundary=/i.test(contentType)) {\n  return new Response('Missing multipart boundary', { status: 400 })\n}","typeGuard":null,"tryCatchPattern":"try {\n  for await (let part of parseMultipartRequest(request)) { /* ... */ }\n} catch (error) {\n  if (error instanceof MultipartParseError && error.message.includes('missing boundary')) {\n    return new Response('Content-Type must include a boundary', { status: 400 })\n  }\n  throw error\n}","preventionTips":["Never hardcode 'Content-Type: multipart/form-data' — let FormData/fetch set it","If constructing headers manually, always append the boundary you actually delimit the body with"],"tags":["multipart","content-type","boundary","http"],"backgroundTag":"multipart-boundary-missing","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}