{"record":{"id":"794dd8c148cc016e","repo":"withastro/astro","slug":"bad-request","errorCode":"BAD_REQUEST","errorMessage":"Failed to serialize request body to JSON. Full error: ${(e as Error).message}","messagePattern":"Failed to serialize request body to JSON\\. Full error: (.+?)","errorType":"error_code","errorClass":"ActionError","httpStatus":400,"severity":"error","filePath":"packages/astro/src/actions/runtime/entrypoints/client.ts","lineNumber":53,"sourceCode":"export const getActionPath = createGetActionPath({\n\tbaseUrl: import.meta.env.BASE_URL,\n\tshouldAppendTrailingSlash,\n});\n\nexport const actions = createActionsProxy({\n\thandleAction: async (param, path) => {\n\t\tconst headers = new Headers();\n\t\theaders.set('Accept', 'application/json');\n\t\t// Apply adapter-specific headers for internal fetches\n\t\tfor (const [key, value] of Object.entries(internalFetchHeaders)) {\n\t\t\theaders.set(key, value);\n\t\t}\n\t\tlet body = param;\n\t\tif (!(body instanceof FormData)) {\n\t\t\ttry {\n\t\t\t\tbody = JSON.stringify(param);\n\t\t\t} catch (e) {\n\t\t\t\tthrow new ActionError({\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t\tmessage: `Failed to serialize request body to JSON. Full error: ${(e as Error).message}`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (body) {\n\t\t\t\theaders.set('Content-Type', 'application/json');\n\t\t\t} else {\n\t\t\t\theaders.set('Content-Length', '0');\n\t\t\t}\n\t\t}\n\t\tconst rawResult = await fetch(\n\t\t\tgetActionPathFromString({\n\t\t\t\tbaseUrl: import.meta.env.BASE_URL,\n\t\t\t\tshouldAppendTrailingSlash,\n\t\t\t\tpath: getActionQueryString(path),\n\t\t\t}),\n\t\t\t{\n\t\t\t\tmethod: 'POST',","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/actions/runtime/entrypoints/client.ts#L35-L71","documentation":"The client action proxy serializes each call argument with JSON.stringify before sending it as the request body. If the argument contains values JSON cannot represent (circular references, functions, Symbols, BigInt, DOM nodes), serialization throws and is wrapped as an ActionError with code BAD_REQUEST (400).","triggerScenarios":"Calling actions.myAction(value) from a client component where value is a DOM event, a React/Vue synthetic event, a class instance with circular refs, a function, a Symbol, an unsupported BigInt, or a DOM element.","commonSituations":"Passing the whole submit event object to an action; passing a component instance or ref; sending a dayjs/Moment object (has methods) instead of a serializable shape; sending FormData to a JSON action instead of a form-accept action.","solutions":["Extract only plain serializable primitives (strings/numbers/booleans/arrays/plain objects/Dates) before calling the action.","If you need to send files or form fields, define the action with accept: 'form' and pass a FormData instance instead.","Strip functions, Symbols, and circular references; convert BigInt to string/number; convert class instances to plain objects."],"exampleFix":"// before\n<form onSubmit={(e) => actions.create({ event: e, extra: () => 1 })} />\n// after\n<form onSubmit={(e) => {\n  const data = new FormData(e.currentTarget);\n  // call a form-accept action, or:\n  actions.create({ title: data.get('title'), count: Number(data.get('count')) });\n}} />","handlingStrategy":"validation","validationCode":"// Validate serializability before calling an action from the client.\nfunction isJsonSerializable(value: unknown): boolean {\n  const seen = new WeakSet();\n  try {\n    JSON.stringify(value, (_k, v) => {\n      if (typeof v === 'object' && v !== null) {\n        if (seen.has(v)) return undefined; // circular -> we still proceed, JSON.stringify throws on cycles\n        seen.add(v);\n      }\n      if (typeof v === 'function' || typeof v === 'symbol' || typeof v === 'bigint') return undefined;\n      return v;\n    });\n    return true;\n  } catch {\n    return false;\n  }\n}\nif (!isJsonSerializable(payload)) throw new Error('Action payload is not JSON-serializable');","typeGuard":"// Reject obviously non-serializable shapes before sending.\nfunction isPlainSerializable(v: unknown): boolean {\n  if (v === null || v === undefined) return true;\n  const t = typeof v;\n  if (t === 'string' || t === 'number' || t === 'boolean') return true;\n  if (t === 'function' || t === 'symbol' || t === 'bigint') return false;\n  if (t !== 'object') return false;\n  if (v instanceof Date || v instanceof URL) return true;\n  if (Array.isArray(v)) return v.every(isPlainSerializable);\n  return Object.values(v as Record<string, unknown>).every(isPlainSerializable);\n}","tryCatchPattern":"try {\n  await actions.myAction(payload);\n} catch (e) {\n  if (e instanceof ActionError && e.code === 'BAD_REQUEST' && /serialize/i.test(e.message)) {\n    showUserError('Please fill the form with valid values.');\n  } else throw e;\n}","preventionTips":["Send only plain objects/arrays/primitives/Dates/URLs to JSON actions.","Use FormData with an accept: 'form' action for files and form fields.","Strip DOM events down to their fields before calling."],"tags":["actions","serialization","client","json"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}