wasmerio/wasmer · error
Cannot store reference type
Error message
Cannot store reference type
What it means
`store` on the closure value wrapper emits Wasm store instructions (`i32_store`, `v128_store`, etc.) into the generated closure trampoline via `InstructionSink`. There is no store instruction encoding for reference types in this closure ABI (refs are unsupported in closures), so hitting `Self::Ref(_)` panics.
Source
Thrown at lib/wasix/src/syscalls/wasix/closure_prepare.rs:82
memory_index,
}),
Self::F32 => sink.f32_store(MemArg {
offset,
align: 0,
memory_index,
}),
Self::F64 => sink.f64_store(MemArg {
offset,
align: 0,
memory_index,
}),
Self::V128 => sink.v128_store(MemArg {
offset,
align: 0,
memory_index,
}),
// Not supported in closures
Self::Ref(_) => panic!("Cannot store reference type"),
};
}
fn load(&self, sink: &mut InstructionSink<'_>, offset: u64, memory_index: u32) {
match self {
Self::I32 => sink.i32_load(MemArg {
offset,
align: 0,
memory_index,
}),
Self::I64 => sink.i64_load(MemArg {
offset,
align: 0,
memory_index,
}),
Self::F32 => sink.f32_load(MemArg {
offset,
align: 0,
memory_index,View on GitHub (pinned to 8c4b9ee9d3)
Solutions
- Restrict closures to scalar-typed parameters/results; adapter-wrap any ref-taking function
- Marshal references as i32 handles through a side table before preparing the closure
- Add a pre-flight type check that rejects `Type::Ref` signatures before invoking `closure_prepare`
- Upgrade or recompile the guest so its closure signatures match the scalar-only closure ABI
Example fix
// before closure_prepare(ctx, func_idx, &[Type::ExternRef], &[])?; // after let handle = side_table.insert(externref_value); closure_prepare(ctx, func_idx, &[Type::I32], &[])?;
Defensive patterns
Strategy: validation
Validate before calling
// Before generating the trampoline, ensure no ref params/results
if params.iter().any(|p| matches!(p, ClosureType::Ref(_)))
|| results.iter().any(|r| matches!(r, ClosureType::Ref(_)))
{
return Err("closure trampoline cannot store reference types".into());
} Type guard
fn is_storable_closure_value(p: &ClosureType) -> bool {
!matches!(p, ClosureType::Ref(_))
} Try / catch
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| param.store(&mut sink, offset, memory_index)));
if result.is_err() { /* ref store: marshal via handle table and regenerate the trampoline */ } Prevention
- Sanitize function types before trampoline generation
- Use i32 handles for references in closure ABIs
- Unit-test codegen against ref-typed signatures to fail early
- Keep runtime and guest closure ABI definitions in sync
When it happens
Trigger: `closure_prepare` generating a trampoline for a function whose parameters include a reference type, so the store-instruction emitter reaches the `Ref` arm; same failure class when materializing parameter stores for externref/funcref/exceptionref values.
Common situations: Dynamically prepared closures over reference-types-proposal functions; codegen pipelines that don't sanitize function types before trampoline generation; guests that were originally written for a runtime whose closure ABI tolerated refs.
Related errors
- Cannot get size of reference type
- Cannot write non-scalar value as bytes
- Cannot read non-scalar value from memory
- Cannot load reference type
- Failed to free closure index: {e}
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/8908b943a7314621.
Report an issue: GitHub.