wasmerio/wasmer · critical

Cannot load reference type

Error message

Cannot load reference type

What it means

During closure_prepare, the WASIX runtime synthesizes a trampoline WASM module whose instructions load/store closure environment fields of a given value type. The ValTypeOps::load helper panics with "Cannot load reference type" because reference types (funcref/externref) cannot be loaded from linear memory. It is an internal invariant panic, not a recoverable error.

Source

Thrown at lib/wasix/src/syscalls/wasix/closure_prepare.rs:113

                memory_index,
            }),
            Self::F32 => sink.f32_load(MemArg {
                offset,
                align: 0,
                memory_index,
            }),
            Self::F64 => sink.f64_load(MemArg {
                offset,
                align: 0,
                memory_index,
            }),
            Self::V128 => sink.v128_load(MemArg {
                offset,
                align: 0,
                memory_index,
            }),
            // Not supported in closures
            Self::Ref(_) => panic!("Cannot load reference type"),
        };
    }
}

/// Build a dynamically linkable WASM module for the given closure.
fn build_closure_wasm_bytes(
    module_name: &str,
    closure: u32,
    backing_function: u32,
    environment_offset: u64,
    argument_types: &[ValType],
    result_types: &[ValType],
) -> Vec<u8> {
    let mut wasm_module = wasm_encoder::Module::new();

    // Add dylink section
    let dylink = CustomSection {
        name: Cow::Borrowed("dylink.0"),

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Change the closure signature so all argument and result types are I32, I64, F32, F64, or V128 only.
  2. If a reference must be passed, store it in an externref table and pass the table index as an i32 instead.
  3. If you control the runtime, extend ValTypeOps::load/store to handle Ref types via table.get/table.set instead of panicking.
  4. Recompile the guest code that declares the closure with reference types removed from the signature.

Example fix

// before (guest side)
closure_allocate(&sig_with_funcref_param);
// after
closure_allocate(&sig_with_i32_table_index_param); // pass table index instead of funcref
Defensive patterns

Strategy: validation

Validate before calling

fn closure_types_supported(arg_types: &[wasm_encoder::ValType], res_types: &[wasm_encoder::ValType]) -> bool {
    let ok = |t: &wasm_encoder::ValType| !matches!(t, wasm_encoder::ValType::Ref(_));
    arg_types.iter().all(ok) && res_types.iter().all(ok)
}

Type guard

fn is_reference_type(t: &wasm_encoder::ValType) -> bool {
    matches!(t, wasm_encoder::ValType::Ref(_))
}

Prevention

When it happens

Trigger: Calling closure_prepare (via the wasix closure_prepare syscall) with a closure whose declared argument or result types include a reference type (Ref/Funcref/Externref) that was not rejected earlier by ValTypeOps::from_u8.

Common situations: Passing funcref or externref parameters when registering a closure with closure_allocate/closure_prepare; using a toolchain that emits reference-typed closure signatures; newer WasmValueType encodings slipping past the from_u8 validation path.

Related errors


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/a37663ff4ae955a4. Report an issue: GitHub.