{"record":{"id":"9470f77f7dc5be34","repo":"vercel/next.js","slug":"invalid-json","errorCode":null,"errorMessage":"Invalid JSON","messagePattern":"Invalid JSON","errorType":"http","errorClass":"ApiError","httpStatus":400,"severity":"error","filePath":"packages/next/src/server/api-utils/node/parse-body.ts","lineNumber":21,"sourceCode":"import { parse } from 'next/dist/compiled/content-type'\nimport isError from '../../../lib/is-error'\nimport type { SizeLimit } from '../../../types'\nimport { ApiError } from '../index'\n\n/**\n * Parse `JSON` and handles invalid `JSON` strings\n * @param str `JSON` string\n */\nfunction parseJson(str: string): object {\n  if (str.length === 0) {\n    // special-case empty json body, as it's a common client-side mistake\n    return {}\n  }\n\n  try {\n    return JSON.parse(str)\n  } catch (e) {\n    throw new ApiError(400, 'Invalid JSON')\n  }\n}\n\n/**\n * Parse incoming message like `json` or `urlencoded`\n * @param req request object\n */\nexport async function parseBody(\n  req: IncomingMessage,\n  limit: SizeLimit\n): Promise<any> {\n  let contentType\n  try {\n    contentType = parse(req.headers['content-type'] || 'text/plain')\n  } catch {\n    contentType = parse('text/plain')\n  }\n  const { type, parameters } = contentType","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/vercel/next.js/blob/0ae8c72462952df163f1b1e0726641bc5b40dc93/packages/next/src/server/api-utils/node/parse-body.ts#L3-L39","documentation":"Thrown as an ApiError(400) by parseJson() when the request body has Content-Type application/json (or application/ld+json) but the body string is not valid JSON. This becomes a 400 Bad Request response to the client. The body parser special-cases empty bodies (returns {}), so only non-empty malformed JSON triggers this.","triggerScenarios":"A POST/PUT/PATCH to an API route with Content-Type: application/json where the raw body fails JSON.parse — e.g. trailing comma, unquoted keys, truncated payload, or text that isn't JSON at all.","commonSituations":"Client sends malformed JSON (typo, truncated by a proxy, wrong content-type header on a form payload); a fetch call with a stringified-but-broken body; curl sending raw text with a JSON content type.","solutions":["On the client, ensure the body is produced via JSON.stringify(obj) and Content-Type is application/json.","In the API route, catch the 400 and return a helpful error message guiding the client.","Validate/normalize the Content-Type; if the client isn't sending JSON, switch to the correct content type."],"exampleFix":"// before (client) - malformed body\nfetch('/api/save', { method: 'POST', headers: { 'content-type': 'application/json' }, body: \"{name:'bad'\" })\n// after\nfetch('/api/save', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ name: 'good' }) })","handlingStrategy":"try-catch","validationCode":"function tryParseJson(body: string): object {\n  try { return JSON.parse(body) }\n  catch { throw new Error('Request body is not valid JSON') }\n}","typeGuard":null,"tryCatchPattern":"// In the API route handler\ntry {\n  // body is already parsed by Next.js; this pattern applies if you parse manually\n} catch (err) {\n  if (err.message === 'Invalid JSON') res.status(400).json({ error: 'Malformed JSON body' })\n}","preventionTips":["On the client, always use JSON.stringify(obj) and set Content-Type: application/json.","Return a clear 400 message guiding clients on the expected format.","Validate Content-Type matches the actual payload."],"tags":["api-routes","body-parsing","json","request-parsing","client-error"],"analyzedSha":"0ae8c72462952df163f1b1e0726641bc5b40dc93","analyzedAt":"2026-08-06T19:44:29.143Z","schemaVersion":2},"datasetVersion":"2026-08-07T01:17:05.418Z"}