remix-run/remix · error · MultipartParseError

Request body is empty

Error message

Request body is empty

What it means

parseMultipartRequest throws MultipartParseError('Request body is empty') when the Request has a null/absent body. Even a correctly-typed multipart request must carry a body stream for the parser to consume, so an empty body is rejected up front rather than producing a confusing parse failure later.

Source

Thrown at packages/multipart-parser/src/lib/multipart-request.ts:45

 * Parse a multipart [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
 * and yield each part as a {@link MultipartPart} object. Useful in HTTP server contexts
 * for handling incoming `multipart/*` requests.
 *
 * @param request The `Request` object containing multipart data
 * @param options Optional parser options, such as `maxHeaderSize`, `maxFileSize`, `maxParts`,
 * and `maxTotalSize`
 * @yields Parsed {@link MultipartPart} objects from the request body
 * @returns An async generator yielding {@link MultipartPart} objects
 */
export async function* parseMultipartRequest(
  request: Request,
  options?: MultipartParserOptions,
): AsyncGenerator<MultipartPart, void, unknown> {
  if (!isMultipartRequest(request)) {
    throw new MultipartParseError('Request is not a multipart request')
  }
  if (!request.body) {
    throw new MultipartParseError('Request body is empty')
  }

  let boundary = getMultipartBoundary(request.headers.get('Content-Type')!)
  if (!boundary) {
    throw new MultipartParseError('Invalid Content-Type header: missing boundary')
  }

  yield* parseMultipartStream(request.body, {
    boundary,
    maxHeaderSize: options?.maxHeaderSize,
    maxFileSize: options?.maxFileSize,
    maxParts: options?.maxParts,
    maxTotalSize: options?.maxTotalSize,
  })
}

View on GitHub (pinned to 9696913134)

Solutions

  1. Check request.body is non-null before parsing
  2. Avoid consuming the body twice — remove prior request.formData()/request.text() calls or clone the request first
  3. Ensure clients actually send a multipart body even for empty forms (FormData with at least one field)

Example fix

// before
let parts = parseMultipartRequest(request) // body already consumed

// after
if (!request.body) {
  return new Response('Empty body', { status: 400 })
}
let parts = parseMultipartRequest(request.clone()) // preserve original for later reads
Defensive patterns

Strategy: validation

Validate before calling

if (request.method !== 'POST' && request.method !== 'PUT') {
  return new Response('Method not allowed', { status: 405 })
}
if (!request.body) {
  return new Response('Empty body', { status: 400 })
}

Try / catch

try {
  for await (let part of parseMultipartRequest(request)) { /* ... */ }
} catch (error) {
  if (error instanceof MultipartParseError && error.message === 'Request body is empty') {
    return new Response('Empty request body', { status: 400 })
  }
  throw error
}

Prevention

When it happens

Trigger: Calling parseMultipartRequest on a Request constructed with no body (or body: null), a GET/HEAD request, or a body already consumed or cancelled by earlier middleware/handler code.

Common situations: Requests whose stream was already read by another parser (e.g. await request.formData() earlier in the handler), hand-constructed Request objects in tests without a body, or intermediaries that buffer and drop the body.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/5098eb305bae3967. Report an issue: GitHub.