{"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/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/ffi/00_ffi.js#L326-L362","documentation":"Deno FFI struct types ({ struct: [...] }) are laid out flat in memory, so every nested field must have a computable size. getTypeSizeAndAlignment marks a struct as 'in progress' (null in its cache) while computing; encountering that marker again means the definition references itself directly or through a cycle, has no finite size, and throws.","triggerScenarios":"A struct object whose struct array contains the same object (directly or via a chain of nested structs): linked-list or tree node definitions that inline themselves; two structs that embed each other; building a type object and later assigning it into its own field array.","commonSituations":"Porting self-referential C structs (struct Node { struct Node *next; }) and naively translating the pointer to a nested struct instead of \"pointer\"; bindings generators that expand types recursively; mutating shared type objects so a field points back to the parent.","solutions":["Model self-references as \"pointer\" fields, matching C's pointer semantics, instead of inline nested structs","Define each struct once and reference other structs by pointer to break cycles","Audit shared type objects for accidental self-assignment (a struct array containing its own container)"],"exampleFix":"// before (C: struct Node { int v; struct Node *next; })\nconst node = { struct: [\"i32\", null] };\nnode.struct[1] = node; // self-reference -> TypeError: recursive struct\n\n// after\nconst node = { struct: [\"i32\", \"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 definition\");\n    seen.add(type);\n    for (const field of type.struct) assertAcyclicStruct(field, seen);\n    seen.delete(type);\n  }\n}\n\nassertAcyclicStruct(MyStruct); // run before passing to FFI","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Represent self-references and links as \"pointer\" fields, never inline structs","Define each struct type exactly once and reference others by pointer","Never insert a type object into its own (or a descendant's) field array"],"tags":["ffi","struct","recursive","type-definition"],"backgroundTag":"recursive-type-definition","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}