{"record":{"id":"6bba5eff02d3580c","repo":"withastro/astro","slug":"cyclic-reference-detected-while-serializing-props","errorCode":null,"errorMessage":"Cyclic reference detected while serializing props for <${metadata.displayName} client:${metadata.hydrate}>!\n\nCyclic references cannot be safely serialized for client-side usage. Please remove the cyclic reference.","messagePattern":"Cyclic reference detected while serializing props for <(.+?) client:(.+?)>!\n\nCyclic references cannot be safely serialized for client-side usage\\. Please remove the cyclic reference\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/runtime/server/serialize.ts","lineNumber":25,"sourceCode":"\tRegExp: 2,\n\tDate: 3,\n\tMap: 4,\n\tSet: 5,\n\tBigInt: 6,\n\tURL: 7,\n\tUint8Array: 8,\n\tUint16Array: 9,\n\tUint32Array: 10,\n\tInfinity: 11,\n};\n\nfunction serializeArray(\n\tvalue: any[],\n\tmetadata: AstroComponentMetadata | Record<string, any> = {},\n\tparents = new WeakSet<any>(),\n): any[] {\n\tif (parents.has(value)) {\n\t\tthrow new Error(`Cyclic reference detected while serializing props for <${metadata.displayName} client:${metadata.hydrate}>!\n\nCyclic references cannot be safely serialized for client-side usage. Please remove the cyclic reference.`);\n\t}\n\tparents.add(value);\n\tconst serialized = value.map((v) => {\n\t\treturn convertToSerializedForm(v, metadata, parents);\n\t});\n\tparents.delete(value);\n\treturn serialized;\n}\n\nfunction serializeObject(\n\tvalue: Record<any, any>,\n\tmetadata: AstroComponentMetadata | Record<string, any> = {},\n\tparents = new WeakSet<any>(),\n): Record<any, any> {\n\tif (parents.has(value)) {\n\t\tthrow new Error(`Cyclic reference detected while serializing props for <${metadata.displayName} client:${metadata.hydrate}>!","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/runtime/server/serialize.ts#L7-L43","documentation":"While serializing component props for an island (`serializeArray`), the array was already present in the `parents` WeakSet — i.e. the array is reachable from itself, forming a cycle. JSON serialization cannot represent cycles, and the client hydration payload would be infinite, so Astro throws. The message names the component (`displayName`) and its hydration directive.","triggerScenarios":"An array passed as a prop to a hydrated component contains a reference back to itself (directly or transitively); a tree/DOM-like structure with parent pointers passed to `client:*` components; a state object mutated to include a back-reference.","commonSituations":"Passing a rich data model (graph, tree with parent links, DOM nodes) as a prop; mutable shared state objects that gained circular references before render.","solutions":["Break the cycle before passing the value: map the data to a flat, acyclic structure (e.g. strip parent pointers).","Use `structuredClone` won't help — clone the data into plain JSON-safe form instead.","Pass only the serializable subset of data the client actually needs."],"exampleFix":"// before — node has a back-reference to its parent array\nconst items = [{ id: 1 }];\nitems[0].parent = items;\n<Tree client:load nodes={items} /> // cyclic -> throws\n\n// after — flatten to acyclic data\nconst items = [{ id: 1, parentId: null }];\n<Tree client:load nodes={items} />","handlingStrategy":"validation","validationCode":"// Reject cyclic arrays before passing them as island props\nfunction assertAcyclic(value: unknown, seen = new WeakSet()): void {\n  if (value && typeof value === 'object') {\n    if (seen.has(value as object)) throw new Error('Cyclic array reference in props');\n    seen.add(value as object);\n    if (Array.isArray(value)) value.forEach((v) => assertAcyclic(v, seen));\n  }\n}","typeGuard":"function isAcyclic(value: unknown, seen = new WeakSet()): boolean {\n  if (value && typeof value === 'object') {\n    if (seen.has(value as object)) return false;\n    seen.add(value as object);\n    if (Array.isArray(value)) return value.every((v) => isAcyclic(v, seen));\n  }\n  return true;\n}","tryCatchPattern":null,"preventionTips":["Pass only plain, JSON-safe data to hydrated components.","Strip parent/back references before rendering.","Validate complex props with a cycle check in dev builds."],"tags":["serialization","hydration","props","cyclic-reference"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}