remix-run/remix · error · MultipartParseError

Request is not a multipart request

Error message

Request is not a multipart request

What it means

parseMultipartRequest(request) validates the incoming web Request before parsing and throws MultipartParseError when its Content-Type is not multipart/form-data or multipart/mixed. This is a precondition failure: the parser only understands multipart bodies, so it refuses to proceed on JSON, urlencoded, or missing content types.

Source

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

}

/**
 * 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. Ensure client forms/requests send Content-Type: multipart/form-data with a boundary (e.g. <form enctype="multipart/form-form-data"> or FormData via fetch, which sets it automatically)
  2. Guard with isMultipartRequest(request) before calling parseMultipartRequest and handle non-multipart requests separately
  3. Check middleware/proxies are not stripping or overriding the Content-Type header

Example fix

// before
let parts = parseMultipartRequest(request)

// after
import { isMultipartRequest, parseMultipartRequest } from 'multipart-parser'
if (!isMultipartRequest(request)) {
  return new Response('Expected multipart request', { status: 415 })
}
let parts = parseMultipartRequest(request)
Defensive patterns

Strategy: validation

Validate before calling

import { isMultipartRequest } from 'multipart-parser'

if (!isMultipartRequest(request)) {
  return new Response('Unsupported media type', { status: 415 })
}

Type guard

import { isMultipartRequest } from 'multipart-parser'
// isMultipartRequest(request: Request): boolean — use as a predicate:
function assertMultipart(request: Request): asserts request is Request {
  if (!isMultipartRequest(request)) throw new Response('Unsupported', { status: 415 })
}

Try / catch

try {
  for await (let part of parseMultipartRequest(request)) { /* ... */ }
} catch (error) {
  if (error instanceof MultipartParseError && error.message.includes('not a multipart')) {
    return new Response('Expected multipart/form-data', { status: 415 })
  }
  throw error
}

Prevention

When it happens

Trigger: Calling parseMultipartRequest with a Request whose Content-Type header is absent or is something like application/json or application/x-www-form-urlencoded; also requests forwarded/proxied with a rewritten Content-Type.

Common situations: A route handler that assumes every POST is multipart but receives fetch submissions with urlencoded bodies; HTML forms missing enctype="multipart/form-data" when file upload is expected; test fixtures built with new Request(url, { body: JSON.stringify(...) }) without a multipart content type.

Related errors


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