honojs/hono · warning · HTTPException
Malformed JSON in request body
Error message
Malformed JSON in request body
What it means
The validator middleware with type 'json' calls c.req.json(), which throws when the request body is not valid JSON. The middleware converts that into an HTTPException with status 400 and the message 'Malformed JSON in request body'.
Source
Thrown at src/validator/validator.ts:102
E extends Env = any,
>(
target: U,
validationFunc: VF
): MiddlewareHandler<E, P, V, ExtractValidationResponse<VF>> => {
return async (c, next) => {
let value = {}
const contentType = c.req.header('Content-Type')
switch (target) {
case 'json':
if (!contentType || !jsonRegex.test(contentType)) {
break
}
try {
value = await c.req.json()
} catch {
const message = 'Malformed JSON in request body'
throw new HTTPException(400, { message })
}
break
case 'form': {
if (
!contentType ||
!(multipartRegex.test(contentType) || urlencodedRegex.test(contentType))
) {
break
}
let formData: FormData
if (c.req.bodyCache.formData) {
formData = await c.req.bodyCache.formData
} else {
try {
const arrayBuffer = await c.req.arrayBuffer()
formData = await bufferToFormData(arrayBuffer, contentType)View on GitHub (pinned to e2740d5a1b)
Solutions
- Reproduce with curl sending the exact body and validate it with a JSON linter
- Ensure the client sets body to JSON.stringify(obj) with Content-Type: application/json
- If the client sends form data, switch the route validator to type 'form' or 'multipart'
- Return a clearer 400 handler via app.onError for HTTPException to expose parse details in dev
Example fix
// before (client)
fetch(url, { body: formData }) // route expects json
// after
fetch(url, { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(obj) }) Defensive patterns
Strategy: validation
Validate before calling
// client-side: ensure valid JSON before sending
const body = JSON.stringify(obj)
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body }) Type guard
const isJsonParseable = (s: string): boolean => { try { JSON.parse(s); return true } catch { return false } } Try / catch
app.onError((err, c) => { if (err instanceof HTTPException && err.status === 400) return c.json({ error: 'bad request', message: err.message }, 400) throw err }) Prevention
- Always pair JSON.stringify with the application/json content type
- Match validator type ('json' vs 'form') to what the client actually sends
- Reject empty bodies early in middleware
- Return actionable 400 messages to clients
When it happens
Trigger: A request with Content-Type: application/json (or no type match for form) whose body is invalid JSON — trailing commas, single quotes, unquoted keys, HTML error pages, empty body, or form data sent with a JSON content type.
Common situations: Frontend sending FormData or urlencoded data while route validates as 'json'; client concatenating JSON fragments; proxies returning HTML error pages; missing body on POST; BOM or text/plain payloads parsed as JSON.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${key} must not contain "\r" or "\n"
- Invalid JSX tag name: ${tag}
- Invalid rule: ${rule}
- invalid JWKS response. "keys" field is not an array
- verifyWithJwks requires options for either "keys" or "jwks_u
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/4e2cc613710fbe20.
Report an issue: GitHub.