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
- Check isMultipartRequest(req) before parsing and route non-multipart requests to a different handler
- Ensure client forms use enctype="multipart/form-data" or clients send FormData
- 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
- Route only file-upload endpoints through the multipart parser
- Use curl -F or FormData clients so content type is correct
- Reject GET/HEAD before parsing
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
- Request is not a multipart request
- Invalid Content-Type header: missing boundary
- Invalid Content-Type header: missing boundary
- Request body is empty
- Multipart boundary exceeds maximum length of ${maxBoundaryLe
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/3faac4eafcf38072.
Report an issue: GitHub.