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
- Always run the module through wasmparser::Validator (or wasmer::Module::validate) before compilation so RefIsNull operands are guaranteed refs
- Upgrade wasmer to keep the value-representation in sync with wasmparser
- Check the producing function's return type is funcref/externref, not i32 faked as a ref
- 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
- Always validate modules — never feed raw sections directly to AOT
- Ensure ref.is_null operands are typed funcref/externref, not integers
- Track wasmparser heap-type representation changes when upgrading
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
- unimplemented operator {operator:?}
- global #{} is a constant
- Cannot load reference type
- Unhandled inner case
- Currently only SIMD instructions are mapped to their return
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/e96e44497a01cc1a.
Report an issue: GitHub.