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
- Change the closure signature so all argument and result types are I32, I64, F32, F64, or V128 only.
- If a reference must be passed, store it in an externref table and pass the table index as an i32 instead.
- If you control the runtime, extend ValTypeOps::load/store to handle Ref types via table.get/table.set instead of panicking.
- 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
- Declare closures using only numeric and vector value types (i32/i64/f32/f64/v128).
- Pass references via an externref/funcref table index instead of by value.
- Check the WasmValueType rejected by from_u8 (Errno::Inval) before preparing the closure.
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
- Failed to free closure index: {e}
- Cannot get size of reference type
- Cannot store reference type
- Failed to load newly built in-memory module: {e}
- unimplemented operator {operator:?}
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/a37663ff4ae955a4.
Report an issue: GitHub.