remix-run/remix · error · MultipartParseError
Invalid multipart stream: missing initial boundary
Error message
Invalid multipart stream: missing initial boundary
What it means
The first bytes of a multipart body must be the opening delimiter (--boundary). If the parser's first chunk doesn't begin with the expected opening boundary, it throws MultipartParseError('Invalid multipart stream: missing initial boundary'), because nothing in the body can be reliably located otherwise.
Source
Thrown at packages/multipart-parser/src/lib/multipart.ts:430
this.#currentHeader = chunk.subarray(index, headerEndIndex)
this.#currentContent = []
this.#contentLength = 0
index = headerEndIndex + 4 // Skip header + \r\n\r\n
this.#state = MultipartParserStateBody
continue
}
if (this.#state === MultipartParserStateStart) {
if (chunkLength < this.#openingBoundaryLength) {
this.#buffer = chunk
break
}
if (this.#findOpeningBoundary(chunk) !== 0) {
throw new MultipartParseError('Invalid multipart stream: missing initial boundary')
}
index = this.#openingBoundaryLength
this.#state = MultipartParserStateAfterBoundary
}
}
}
#append(chunk: Uint8Array): void {
if (chunk.length === 0) {
return
}
if (this.#contentLength + chunk.length > this.maxFileSize) {
throw new MaxFileSizeExceededError(this.maxFileSize)
}
View on GitHub (pinned to 9696913134)
Solutions
- Verify the boundary in the Content-Type exactly matches the --boundary delimiters in the raw body (dump the first ~100 bytes)
- Ensure the Content-Type header is generated by the same FormData/client that built the body — never mix manual headers with generated bodies
- Remove any preamble before the first --boundary if you control the sender
Example fix
// before
headers: { 'Content-Type': `multipart/form-data; boundary=${myBoundary}` }
body: `--${otherBoundary}\r\n...` // mismatch throws
// after
let body = `--${myBoundary}\r\nContent-Disposition: form-data; name="f"\r\n\r\nv\r\n--${myBoundary}--\r\n`
headers: { 'Content-Type': `multipart/form-data; boundary=${myBoundary}` } Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check the first bytes before parsing (when body is buffered)
let head = new TextDecoder().decode(bodyBytes.subarray(0, boundary.length + 4))
if (!head.startsWith(`--${boundary}`)) {
throw new Response('Body does not start with multipart boundary', { status: 400 })
} Try / catch
try {
for await (let part of parseMultipartStream(stream, { boundary })) { /* ... */ }
} catch (error) {
if (error instanceof MultipartParseError && error.message.includes('missing initial boundary')) {
return new Response('Malformed multipart body', { status: 400 })
}
throw error
} Prevention
- Generate Content-Type and body from the same FormData object
- Dump the first bytes of failing bodies to spot boundary mismatches fast
When it happens
Trigger: A body that starts with a preamble or garbage instead of --boundary; a boundary extracted from the Content-Type that doesn't match the actual body delimiters (e.g. extra quotes, case differences, or URL-encoding); truncated or restarted uploads.
Common situations: Content-Type boundary value not matching the body (proxy mangling, manual header construction), clients sending a nonstandard preamble, or a partially-consumed body stream being reparsed.
Related errors
- Invalid Content-Type header: missing boundary
- Invalid Content-Type header: missing boundary
- Multipart boundary exceeds maximum length of ${maxBoundaryLe
- Request is not a multipart request
- Request body is empty
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/1ff4f6fe291b8de7.
Report an issue: GitHub.