denoland/deno · error · TypeError

Foreign symbol of type 'void' is not supported

Error message

Foreign symbol of type 'void' is not supported

What it means

In the Deno.dlopen symbol table, an entry carrying a `type` field requests a static value read from the loaded library (op_ffi_get_static) — i.e. an exported data symbol. 'void' denotes no value at all, so it is rejected up front with a TypeError. Function symbols use { parameters, result } instead and never take a `type` field.

Source

Thrown at ext/ffi/00_ffi.js:477

  symbols = { __proto__: null };

  constructor(path, symbols) {
    ({ 0: this.#rid, 1: this.symbols } = op_ffi_load(path, symbols));
    for (const symbol in symbols) {
      if (!ObjectHasOwn(symbols, symbol)) {
        continue;
      }

      // Symbol was marked as optional, and not found.
      // In that case, we set its value to null in Rust-side.
      if (symbols[symbol] === null) {
        continue;
      }

      if (ReflectHas(symbols[symbol], "type")) {
        const type = symbols[symbol].type;
        if (type === "void") {
          throw new TypeError(
            "Foreign symbol of type 'void' is not supported",
          );
        }

        const name = symbols[symbol].name || symbol;
        const value = op_ffi_get_static(
          this.#rid,
          name,
          type,
          symbols[symbol].optional,
        );
        ObjectDefineProperty(
          this.symbols,
          symbol,
          {
            __proto__: null,
            configurable: false,
            enumerable: true,

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Delete the symbol entry (or its type field) instead of stubbing it with void
  2. Use the real C type of the exported datum: u32, i32, f64, pointer, etc.
  3. For exported functions, declare { parameters: [...], result: ... } rather than { type: ... }
  4. Use optional: true only for symbols that may be missing in the library, not as a substitute for a valid type

Example fix

// before
const lib = Deno.dlopen("./libexample.so", {
  VERSION: { name: "VERSION", type: "void" },
});

// after
const lib = Deno.dlopen("./libexample.so", {
  VERSION: { name: "VERSION", type: "u32" }, // actual C type of the exported variable
});
Defensive patterns

Strategy: validation

Validate before calling

const VALID_STATIC_TYPES = new Set(["u8","u16","u32","u64","i8","i16","i32","i64","f32","f64","usize","isize","pointer"]);
function validateStaticSymbols(symbols) {
  for (const [key, def] of Object.entries(symbols)) {
    if (def && typeof def === "object" && "type" in def && !VALID_STATIC_TYPES.has(def.type)) {
      throw new Error(`symbol '${key}' has unsupported static type '${def.type}'`);
    }
  }
}
validateStaticSymbols(symbols);
const lib = Deno.dlopen(path, symbols);

Type guard

const VALID_STATIC_TYPES = new Set(["u8","u16","u32","u64","i8","i16","i32","i64","f32","f64","usize","isize","pointer"]);
function isStaticSymbolDef(def: unknown): boolean {
  return typeof def === "object" && def !== null && "type" in def &&
    VALID_STATIC_TYPES.has((def as { type: string }).type);
}

Try / catch

try {
  lib = Deno.dlopen(path, symbols);
} catch (err) {
  if (err instanceof TypeError && err.message === "Foreign symbol of type 'void' is not supported") {
    // drop or retype the void entry (it names a data symbol with no value)
  } else throw err;
}

Prevention

When it happens

Trigger: Deno.dlopen(lib, { VERSION: { name: "VERSION", type: "void" } }) — any symbol object whose type is exactly "void".

Common situations: Auto-generated binding files from C headers that include void macros or placeholder entries; misunderstanding that `type` is for exported variables; hand-writing a stub entry to 'skip' a symbol.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/5ba3c6e0f522e872. Report an issue: GitHub.