{"record":{"id":"53d521c72db5bc45","repo":"Budibase/budibase","slug":"invalid-json-body","errorCode":null,"errorMessage":"Invalid JSON body","messagePattern":"Invalid JSON body","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/frontend-core/src/api/index.ts","lineNumber":181,"sourceCode":"    let headers: Headers = { Accept: \"application/json\" }\n    headers[Header.SESSION_ID] = APISessionID\n    if (!external) {\n      headers[Header.API_VER] = ApiVersion\n    }\n    if (json) {\n      headers[\"Content-Type\"] = \"application/json\"\n    }\n    if (config?.attachHeaders) {\n      config.attachHeaders(headers, { url, method })\n    }\n\n    // Build request body\n    let requestBody: any = body\n    if (json) {\n      try {\n        requestBody = JSON.stringify(body)\n      } catch (error) {\n        throw makeError(\"Invalid JSON body\", url, method)\n      }\n    }\n\n    // Make request\n    let response: Response\n    try {\n      response = await fetch(url, {\n        method,\n        headers,\n        body: requestBody,\n        credentials: \"same-origin\",\n        signal,\n      })\n    } catch (error) {\n      delete cache[url]\n      if (signal?.aborted) {\n        throw error\n      }","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/frontend-core/src/api/index.ts#L163-L199","documentation":"makeApiCall in frontend-core serializes the request body with JSON.stringify when the call is marked as JSON. If serialization throws — typically because the body contains a circular reference or BigInt — it throws an APIError with message 'Invalid JSON body', status 400, plus the url and method. This is a client-side pre-flight failure; no request is ever sent to the server.","triggerScenarios":"Calling any frontend-core API wrapper with json:true (non-GET) where body contains circular object references, BigInt values, or functions that JSON.stringify cannot serialize.","commonSituations":"Passing a Svelte store object or a component/props graph with back-references as the body; including BigInt ids from some ORMs; accidentally passing FormData into a JSON call.","solutions":["Inspect the body for circular references and send only plain serializable data.","Convert BigInt values to strings or numbers before the call.","Strip non-serializable fields (functions, DOM nodes, class instances) by mapping to a plain object.","As a debug aid, try JSON.stringify(body) in a try/catch where the call is made to reproduce the failure."],"exampleFix":"// before: circular reference\napi.post('/rows', { table, parent: row, row }) // row.parent === table\n// after: send plain data\napi.post('/rows', { tableId: table._id, name: row.name })","handlingStrategy":"type-guard","validationCode":"function isSerializable(v: unknown): boolean {\n  try { JSON.stringify(v); return true } catch { return false }\n}\nif (!isSerializable(body)) throw new Error(\"Body is not JSON-serializable\")","typeGuard":"const isPlainBody = (b: unknown): b is Record<string, unknown> =>\n  typeof b === \"object\" && b !== null && !Array.isArray(b) === false ||\n  (typeof b === \"object\" && b !== null && Object.getPrototypeOf(b) === Object.prototype)","tryCatchPattern":"try {\n  await api.post(url, body)\n} catch (e) {\n  if (e?.message === \"Invalid JSON body\") {\n    console.error(\"Request body for\", e.url, e.method, \"is not serializable\")\n  }\n}","preventionTips":["Send plain objects/arrays only — strip class instances and stores","Convert BigInt values to strings before sending","Never reuse request/response objects that hold circular references","Test bodies with JSON.stringify in dev when payloads are dynamic"],"tags":["api","json","serialization","frontend"],"backgroundTag":"json-serialization-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}