denoland/deno · error · TypeError
Cannot get pointer size: found recursive struct
Error message
Cannot get pointer size: found recursive struct
What it means
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.
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)
Example fix
// before (C: struct Node { int v; struct Node *next; })
const node = { struct: ["i32", null] };
node.struct[1] = node; // self-reference -> TypeError: recursive struct
// after
const node = { struct: ["i32", "pointer"] }; Defensive patterns
Strategy: validation
Validate before calling
function assertAcyclicStruct(type, seen = new Set()) {
if (typeof type === "object" && type !== null && type.struct) {
if (seen.has(type)) throw new Error("recursive FFI struct definition");
seen.add(type);
for (const field of type.struct) assertAcyclicStruct(field, seen);
seen.delete(type);
}
}
assertAcyclicStruct(MyStruct); // run before passing to FFI Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot get pointer size, unsupported type
- Cannot access pointer: expected 'ArrayBuffer'…
- Cannot construct UnsafeCallback: cannot be nonblocking
- ERR_FS_EISDIR
- ERR_INVALID_ARG_VALUE
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/8cc616f8fa89e0dc.
Report an issue: GitHub.
Appendix: source
Thrown at ext/ffi/00_ffi.js:344
buffer,
);
return buffer;
}
}
}
}
function isStruct(type) {
return typeof type === "object" && type !== null &&
typeof type.struct === "object";
}
function getTypeSizeAndAlignment(type, cache = new SafeMap()) {
if (isStruct(type)) {
const cached = cache.get(type);
if (cached !== undefined) {
if (cached === null) {
throw new TypeError(
"Cannot get pointer size: found recursive struct",
);
}
return cached;
}
cache.set(type, null);
let size = 0;
let alignment = 1;
for (const field of new SafeArrayIterator(type.struct)) {
const { 0: fieldSize, 1: fieldAlign } = getTypeSizeAndAlignment(
field,
cache,
);
alignment = MathMax(alignment, fieldAlign);
size = MathCeil(size / fieldAlign) * fieldAlign;
size += fieldSize;
}
size = MathCeil(size / alignment) * alignment;View on GitHub (pinned to 9ad36f7a2c)