{"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/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/runtime/server/serialize.ts#L7-L43","documentation":"Props given to a component with a client:* hydration directive must be serialized into the HTML payload so the component can rehydrate in the browser. During array serialization Astro tracks already-visited objects with a WeakSet; if an array contains itself (directly or through a nested value), serialization aborts with this cyclic-reference error because such a structure has no JSON representation. The component name and directive in the message identify exactly which props are affected.","triggerScenarios":"Passing a self-referential array (const a = []; a.push(a)) to a component with client:load/client:visible/etc.; passing a tree or linked structure where a nested array points back to an object that contains it; class instances or ORM models with parent back-references inside an array prop.","commonSituations":"Category trees with children and parent links; query results with circular relations; state stores that expose self-referencing objects; code that worked when the component rendered server-only and broke after adding a client:* directive.","solutions":["Remove the cycle before passing props — clone into plain data and drop back-references (e.g. delete node.parent)","Map the structure to a serializable DTO (ids instead of object references) and rebuild relationships on the client","If the cyclic data is only needed during SSR, remove the client:* directive so props are never serialized"],"exampleFix":"---\n// before\nconst items = ['a', 'b'];\nitems.push(items); // cyclic!\n---\n<Counter items={items} client:load />\n\n---\n// after\n---\n<Counter items={['a', 'b']} client:load />","handlingStrategy":"validation","validationCode":"function hasCycle(value: unknown, path: WeakSet<object> = new WeakSet()): boolean {\n\tif (value === null || typeof value !== 'object') return false;\n\tif (path.has(value as object)) return true;\n\tpath.add(value as object);\n\tconst cyclic = (Array.isArray(value) ? value : Object.values(value)).some((v) => hasCycle(v, path));\n\tpath.delete(value as object);\n\treturn cyclic;\n}\n\nif (hasCycle(items)) {\n\tthrow new Error('items contains a cycle — strip back-references before client:* hydration');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass plain serializable data (DTOs with ids) to hydrated components instead of live object graphs","Strip parent/back pointers when converting trees for props: const toProp = (n) => ({ ...n, parent: undefined, children: n.children.map(toProp) })","Remember JSON.stringify fails on the same shapes — if it cannot be JSON-stringified, it cannot cross a client:* boundary"],"tags":["serialization","props","client-directive","cyclic-reference"],"backgroundTag":"json-cyclic-reference","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}