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

  1. Restrict closures to scalar-typed parameters/results; adapter-wrap any ref-taking function
  2. Marshal references as i32 handles through a side table before preparing the closure
  3. Add a pre-flight type check that rejects `Type::Ref` signatures before invoking `closure_prepare`
  4. 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

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


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