vercel/next.js · error · ApiError
Invalid body
Error message
Invalid body
What it means
Thrown by parseBody() while reading the raw request body for an API route / Server Action. It is a 400 ApiError raised when getRawBody() rejects for any reason OTHER than the 'entity.too.large' size-limit error (which separately yields a 413). It signals the incoming request body could not be consumed at all -- malformed Content-Encoding, unsupported/invalid charset, a prematurely-closed/truncated stream, or a bad encoding parameter.
Source
Thrown at packages/next/src/server/api-utils/node/parse-body.ts:52
try {
contentType = parse(req.headers['content-type'] || 'text/plain')
} catch {
contentType = parse('text/plain')
}
const { type, parameters } = contentType
const encoding = parameters.charset || 'utf-8'
let buffer
try {
const getRawBody =
require('next/dist/compiled/raw-body') as typeof import('next/dist/compiled/raw-body')
buffer = await getRawBody(req, { encoding, limit })
} catch (e) {
if (isError(e) && e.type === 'entity.too.large') {
throw new ApiError(413, `Body exceeded ${limit} limit`)
} else {
throw new ApiError(400, 'Invalid body')
}
}
const body = buffer.toString()
if (type === 'application/json' || type === 'application/ld+json') {
return parseJson(body)
} else if (type === 'application/x-www-form-urlencoded') {
const qs = require('querystring') as typeof import('querystring')
return qs.decode(body)
} else {
return body
}
}
View on GitHub (pinned to 0ae8c72462)
Solutions
- Inspect the exact error cause: the raw-body error is re-thrown, so check server logs for the underlying message to determine if it's an encoding, charset, or stream issue.
- Verify the client sends a valid Content-Type with a supported charset (e.g. 'application/json; charset=utf-8') and a consistent Content-Encoding.
- If a reverse proxy or middleware is involved, confirm it is not truncating or double-encoding the request body.
- Ensure the client is not closing the connection before the full body is transmitted (increase client-side timeout for large uploads).
Example fix
// before: client sends gzip body without server decompression
// fetch(url, { body: gzip(json), headers: { 'content-encoding': 'gzip' } })
// after: send uncompressed JSON or decompress in middleware
// fetch(url, { body: json, headers: { 'content-type': 'application/json' } }) Defensive patterns
Strategy: validation
Validate before calling
// Before calling parseBody, sanity-check the content-type/encoding.
// parseBody itself reads content-type; validate upstream in middleware:
export function isValidBodyRequest(req) {
const ct = req.headers['content-type'] || ''
if (!ct) return false
const enc = req.headers['content-encoding']
// reject encodings you don't handle
if (enc && !['identity'].includes(enc.toLowerCase())) return false
return true
} Type guard
function isReadableBody(req): req is import('http').IncomingMessage & { readable: true } {
return Boolean(req && typeof (req as any).on === 'function' && (req as any).readable !== false)
} Try / catch
try {
const body = await parseBody(req, limit)
} catch (e) {
if (e instanceof ApiError && e.statusCode === 400) {
res.status(400).json({ error: 'Malformed request body' })
} else if (e instanceof ApiError && e.statusCode === 413) {
res.status(413).json({ error: 'Body too large' })
} else {
throw e
}
} Prevention
- Document and enforce a fixed Content-Type for each API route.
- Reject or decompress unsupported Content-Encoding in middleware before parsing.
- Cap request sizes at the proxy/load-balancer to fail fast.
- Log the underlying raw-body error to distinguish encoding vs stream failures.
When it happens
Trigger: A POST request hits an API route or Server Action handler that calls parseBody(req, limit). The Content-Type header carries a charset/encoding that raw-body rejects, the client sends a body with a content-encoding the server can't decode (e.g. gzip without decompression), or the connection drops mid-stream so getRawBody throws a non-size error. The catch at parse-body.ts:48 checks e.type === 'entity.too.large'; anything else falls through to this 400.
Common situations: A proxy/CDN injects or strips Content-Encoding causing the body to be unintelligible; a client posts an empty body with a charset like 'utf-32'; reverse-proxy timeouts truncate the upload; or a misconfigured fetch sets a wrong charset parameter. Also seen when the content-type header itself is unparseable (handled by the fallback at line 36-38) yet the body is still unreadable.
Related errors
- Failed to fetch ${url}, too many retries
- Invalid redirect arguments. Please use a single argument URL
- Preview data is limited to 2KB currently, reduce how much da
- Invalid urlPath provided to revalidate(), must be a path e.g
- Invalid response ${res.status}
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/94aa15b34713feac.
Report an issue: GitHub.