wasmerio/wasmer · error

ref.is_null only accepts reference types

Error message

ref.is_null only accepts reference types

What it means

`unreachable!("ref.is_null only accepts reference types")` fires in translate_reference_operator when the value pushed for a RefIsNull operand is neither a FuncValue/externref-style reference, pointer, nor typed-reference value the backend recognizes. The translator's internal value representation didn't match any expected reference-kind LLVM value.

Source

Thrown at lib/compiler-llvm/src/translator/code.rs:11415

        match op {
            Operator::RefNull { hty } => {
                let ty = err!(wpheaptype_to_type(hty));
                let ty = type_to_llvm(self.intrinsics, ty)?;
                self.state.push1(ty.const_zero());
            }
            Operator::RefIsNull => {
                let value = self.state.pop1()?;
                let is_null = match value {
                    BasicValueEnum::IntValue(value) => err!(self.builder.build_int_compare(
                        IntPredicate::EQ,
                        value,
                        value.get_type().const_zero(),
                        "",
                    )),
                    BasicValueEnum::PointerValue(value) => {
                        err!(self.builder.build_is_null(value, ""))
                    }
                    _ => unreachable!("ref.is_null only accepts reference types"),
                };
                let is_null = err!(self.builder.build_int_z_extend(
                    is_null,
                    self.intrinsics.i32_ty,
                    ""
                ));
                self.state.push1(is_null);
            }
            Operator::RefFunc { function_index } => {
                let index = self
                    .intrinsics
                    .i32_ty
                    .const_int(function_index.into(), false);
                let value = self
                    .build_call_with_param_attributes(
                        self.intrinsics.func_ref,
                        &[self.ctx.basic().into(), index.into()],
                        "",

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Always run the module through wasmparser::Validator (or wasmer::Module::validate) before compilation so RefIsNull operands are guaranteed refs
  2. Upgrade wasmer to keep the value-representation in sync with wasmparser
  3. Check the producing function's return type is funcref/externref, not i32 faked as a ref
  4. Patch the arm to return a CompileError instead of unreachable! if maintaining a fork

Example fix

// before
Module::new(&store, unvalidated_bytes); // bad ref operand reaches backend
// after
wasmparser::Validator::new_with_features(features).validate_all(&bytes)?;
Module::new(&store, bytes)?;
Defensive patterns

Strategy: validation

Validate before calling

wasmparser::Validator::new_with_features(WasmFeatures {
    reference_types: true, ..Default::default()
}).validate_all(&bytes)?; // guarantees ref.is_null operands are reference types

Type guard

fn is_reference_type(t: &wasmparser::HeapType) -> bool {
    matches!(t.composite_type(), None) || matches!(t, wasmparser::HeapType::Func | wasmparser::HeapType::Extern | wasmparser::HeapType::Any)
}

Prevention

When it happens

Trigger: RefIsNull applied to a value whose translated LLVM type is not a reference (funcref/externref) — e.g. after a canonicalization bug, a mis-validated module feeding a non-ref operand, or enum/type-representation changes between wasmparser and the translator.

Common situations: Modules that skip validation (custom loaders, AOT pipelines feeding raw sections); wasmparser version skew changing how heap types are represented; hand-crafted wasm.

Related errors


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