payloadcms/payload · error · APIError

Invalid JSON

Error message

Invalid JSON

What it means

Thrown during request body parsing when the `Content-Type` is `application/json` but `JSON.parse` raises a `SyntaxError`. Payload reads the raw body text and parses it before hooks run, so malformed JSON never reaches your field-level logic.

Source

Thrown at packages/payload/src/utilities/addDataAndFileToRequest.ts:30

 */
export const addDataAndFileToRequest: AddDataAndFileToRequest = async (req) => {
  const { body, headers, method, payload } = req

  if (method && ['PATCH', 'POST', 'PUT'].includes(method.toUpperCase()) && body) {
    const [contentType] = (headers.get('Content-Type') || '').split(';', 1)
    const bodyByteSize = parseInt(req.headers.get('Content-Length') || '0', 10)
    const hasBodyStream = req.body !== null

    if (contentType === 'application/json') {
      try {
        const text = await req.text?.()
        const data = text ? JSON.parse(text) : {}
        req.data = data
        // @ts-expect-error attach json method to request
        req.json = () => Promise.resolve(data)
      } catch (error) {
        if (error instanceof SyntaxError) {
          throw new APIError('Invalid JSON', 400)
        }
        req.payload.logger.error(error)
        throw error
      }
    } else if ((bodyByteSize || hasBodyStream) && contentType?.includes('multipart/')) {
      const { error, fields, files } = await processMultipartFormdata({
        options: {
          ...(payload.config.bodyParser || {}),
          ...(payload.config.upload || {}),
        },
        request: req as Request,
      })

      if (error) {
        throw new APIError(error.message)
      }

      // Set all files on req.files for access by hooks

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Ensure the client sends strict JSON: double-quoted keys, no trailing commas, no comments.
  2. Use `JSON.stringify(obj)` (JS) or the language canonical JSON serializer -- do not send object literals.
  3. Verify the full body reaches the server (check `Content-Length`, proxy buffering, no truncation).
  4. Test the body with a JSON linter before sending.

Example fix

// before
await fetch('/api/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: { name: 'Jane' }, // raw object -- sent as '[object Object]'
})

// after
await fetch('/api/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Jane' }),
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate JSON before sending
function safeJsonStringify(obj) {
  return JSON.stringify(obj)
}
const body = safeJsonStringify(payload)
JSON.parse(body) // round-trip check
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body })

Try / catch

try {
  await fetch(url, { method: 'POST', body: JSON.stringify(data), headers: jsonHeaders })
} catch (e) {
  if (e instanceof APIError && e.message === 'Invalid JSON') {
    // log the raw body and fix the serialization
  } else throw e
}

Prevention

When it happens

Trigger: Any POST/PATCH/PUT request with `Content-Type: application/json` whose body is not valid JSON: trailing commas, unquoted keys, single-quoted strings, truncated payload, or BOM-prefixed content.

Common situations: Client sends a JS object literal (not strict JSON) e.g. `{ key: "value" }` or `{a: 1,}`; a proxy truncated the body; `fetch` was called with `body: obj` instead of `body: JSON.stringify(obj)`; hand-crafted curl with unbalanced braces.

Understand the failure class

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/6a9907b0285db110. Report an issue: GitHub.