{"record":{"id":"8cc616f8fa89e0dc","repo":"denoland/deno","slug":"cannot-get-pointer-size-found-recursive-struct","errorCode":null,"errorMessage":"Cannot get pointer size: found recursive struct","messagePattern":"Cannot get pointer size: found recursive struct","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/ffi/00_ffi.js","lineNumber":344,"sourceCode":"          buffer,\n        );\n        return buffer;\n      }\n    }\n  }\n}\n\nfunction isStruct(type) {\n  return typeof type === \"object\" && type !== null &&\n    typeof type.struct === \"object\";\n}\n\nfunction getTypeSizeAndAlignment(type, cache = new SafeMap()) {\n  if (isStruct(type)) {\n    const cached = cache.get(type);\n    if (cached !== undefined) {\n      if (cached === null) {\n        throw new TypeError(\n          \"Cannot get pointer size: found recursive struct\",\n        );\n      }\n      return cached;\n    }\n    cache.set(type, null);\n    let size = 0;\n    let alignment = 1;\n    for (const field of new SafeArrayIterator(type.struct)) {\n      const { 0: fieldSize, 1: fieldAlign } = getTypeSizeAndAlignment(\n        field,\n        cache,\n      );\n      alignment = MathMax(alignment, fieldAlign);\n      size = MathCeil(size / fieldAlign) * fieldAlign;\n      size += fieldSize;\n    }\n    size = MathCeil(size / alignment) * alignment;","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/ffi/00_ffi.js#L326-L362","documentation":"Struct types used in FFI (e.g. as nonblocking call results, where UnsafeFnPointer computes sizes) are laid out by recursively summing field sizes/alignments with a per-computation cache. A struct that contains itself directly or through another struct has no finite size; the recursion is detected via a cache sentinel and reported as a TypeError. This mirrors C compilers rejecting infinitely-sized struct definitions.","triggerScenarios":"Defining a type object whose struct array references the same object (const node = { struct: [\"pointer\", node] }) or a cycle through multiple structs (A contains B, B contains A), then using it where layout is needed.","commonSituations":"Modeling linked-list or tree nodes from C headers; building type objects mutably and accidentally closing a cycle; splitting one logical struct into mutually referencing parts.","solutions":["Replace the self-reference with pointer indirection: type the field as \"pointer\" (struct node *next in C), not the struct itself","Break mutual cycles the same way: at least one edge in the cycle must be a pointer","Re-check that nested struct fields are { struct: [...] } objects rather than accidental back-references"],"exampleFix":"// before\nconst node = { struct: [\"u32\"] };\nnode.struct.push(node); // recursive struct\n\n// after\nconst node = {\n  struct: [\"u32\", { struct: [\"u32\"] } /* inline next->val */],\n};\n// C: struct node { uint32_t val; struct node *next; } -> model next as \"pointer\":\nconst nodeFixed = { struct: [\"u32\", \"pointer\"] };","handlingStrategy":"validation","validationCode":"function assertAcyclicStruct(type, seen = new Set()) {\n  if (typeof type === \"object\" && type !== null && type.struct) {\n    if (seen.has(type)) throw new Error(\"recursive FFI struct type\");\n    seen.add(type);\n    for (const field of type.struct) assertAcyclicStruct(field, seen);\n  }\n}\nassertAcyclicStruct(myStructType); // run before handing it to FFI","typeGuard":null,"tryCatchPattern":"try {\n  const fn = new Deno.UnsafeFnPointer(ptr, { parameters: [], result: myStructType });\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"Cannot get pointer size: found recursive struct\") {\n    // replace the self-reference with \"pointer\" and rebuild the type object\n  } else throw err;\n}","preventionTips":["Model C self-references as \"pointer\" fields, never as the struct itself","Build struct types as fresh literals (no mutation of a const you later nest)","Run a cheap cycle check over type objects when types come from data or generators","Remember mutually recursive structs are equally invalid"],"tags":["ffi","struct","recursion","layout"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}