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

  1. Model self-references as "pointer" fields, matching C's pointer semantics, instead of inline nested structs
  2. Define each struct once and reference other structs by pointer to break cycles
  3. 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

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


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)