remix-run/remix · error · MultipartParseError

Request is not a multipart request

Error message

Request is not a multipart request

What it means

The Node-specific parseMultipartRequest(req) variant throws MultipartParseError when the http.IncomingMessage is not a multipart request, i.e. its content-type header is not multipart/form-data or multipart/mixed. It mirrors the web Request variant but reads req.headers['content-type'] from a raw Node server request.

Source

Thrown at packages/multipart-parser/src/lib/multipart.node.ts:74

  let contentType = req.headers['content-type']
  return contentType != null && /^multipart\//i.test(contentType)
}

/**
 * Parse a multipart Node.js request and yield each part as a {@link MultipartPart} object.
 *
 * @param req The Node.js `http.IncomingMessage` object containing multipart data
 * @param options Options for the parser, such as `maxHeaderSize`, `maxFileSize`, `maxParts`,
 * and `maxTotalSize`
 * @yields Parsed {@link MultipartPart} objects from the multipart request body
 * @returns An async generator yielding {@link MultipartPart} objects
 */
export async function* parseMultipartRequest(
  req: http.IncomingMessage,
  options?: MultipartParserOptions,
): AsyncGenerator<MultipartPart, void, unknown> {
  if (!isMultipartRequest(req)) {
    throw new MultipartParseError('Request is not a multipart request')
  }

  let boundary = getMultipartBoundary(req.headers['content-type']!)
  if (!boundary) {
    throw new MultipartParseError('Invalid Content-Type header: missing boundary')
  }

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

View on GitHub (pinned to 9696913134)

Solutions

  1. Check isMultipartRequest(req) before parsing and route non-multipart requests to a different handler
  2. Ensure client forms use enctype="multipart/form-data" or clients send FormData
  3. Verify the content-type header survives any Node middleware (e.g. body-parser running first)

Example fix

// before
for await (let part of parseMultipartRequest(req)) { ... }

// after
import { isMultipartRequest } from 'multipart-parser'
if (!isMultipartRequest(req)) {
  res.writeHead(415).end('Expected multipart request')
  return
}
for await (let part of parseMultipartRequest(req)) { ... }
Defensive patterns

Strategy: validation

Validate before calling

import { isMultipartRequest } from 'multipart-parser'

if (!isMultipartRequest(req)) {
  res.writeHead(415, { 'Content-Type': 'text/plain' })
  return res.end('Expected multipart/form-data')
}

Type guard

import { isMultipartRequest } from 'multipart-parser'

const isMultipart = (req: http.IncomingMessage): boolean => isMultipartRequest(req)

Try / catch

try {
  for await (let part of parseMultipartRequest(req)) { /* ... */ }
} catch (error) {
  if (error instanceof MultipartParseError && error.message.includes('not a multipart')) {
    res.writeHead(415).end('Expected multipart request')
    return
  }
  throw error
}

Prevention

When it happens

Trigger: Calling the Node parseMultipartRequest with a req whose method has no multipart body (GET), whose content-type is application/json or urlencoded, or whose header casing/content differs from what isMultipartRequest expects.

Common situations: Express/Node http servers routing all POSTs into multipart parsing even when clients submit JSON or urlencoded forms; HTML forms missing enctype="multipart/form-data"; health-check or prefetch requests hitting the upload endpoint.

Related errors


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