wasmerio/wasmer · error
internal error: entered unreachable code
Error message
internal error: entered unreachable code
What it means
A bare `unreachable!()` panic in the signed-division/remainder lowering of `translate_integer_arithmetic_operator` (lib/compiler-llvm/src/translator/code.rs). The code builds i32::MIN / -1 and i64::MIN / -1 overflow-trap constants and asserts the operand's LLVM int type is exactly i32 or i64; any other type (i8/i16/i128/floating) hitting the srem/sdiv path violates the translator's invariant and aborts with "internal error: entered unreachable code".
Source
Thrown at lib/compiler-llvm/src/translator/code.rs:4962
let res = err!(self.builder.build_int_unsigned_div(v1, v2, ""));
self.state.push1(res);
}
Operator::I32RemS | Operator::I64RemS => {
let ((v1, i1), (v2, i2)) = self.state.pop2_extra()?;
let v1 = self.apply_pending_canonicalization(v1, i1)?;
let v2 = self.apply_pending_canonicalization(v2, i2)?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let int_type = v1.get_type();
let (min_value, neg_one_value) = if int_type == self.intrinsics.i32_ty {
let min_value = int_type.const_int(i32::MIN as u64, false);
let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false);
(min_value, neg_one_value)
} else if int_type == self.intrinsics.i64_ty {
let min_value = int_type.const_int(i64::MIN as u64, false);
let neg_one_value = int_type.const_int(-1i64 as u64, false);
(min_value, neg_one_value)
} else {
unreachable!()
};
self.trap_if_zero(v2)?;
// "Overflow also leads to undefined behavior; this is a rare
// case, but can occur, for example, by taking the remainder of
// a 32-bit division of -2147483648 by -1. (The remainder
// doesn’t actually overflow, but this rule lets srem be
// implemented using instructions that return both the result
// of the division and the remainder.)"
// -- https://llvm.org/docs/LangRef.html#srem-instruction
//
// In Wasm, the i32.rem_s i32.const -2147483648 i32.const -1 is
// i32.const 0. We implement this by swapping out the left value
// for 0 in this case.
let will_overflow = err!(self.builder.build_and(
err!(self.builder.build_int_compare(
IntPredicate::EQ,View on GitHub (pinned to 8c4b9ee9d3)
Solutions
- Use an official released wasmtime version that matches the feature set of your modules (enable the `wide-arithmetic` Cargo feature or upgrade, rather than patching the div/rem path ad hoc).
- If you modified the translator, ensure new integer types (i128) are handled before this arm, e.g. lower wide div/rem in the I64Add128/I64MulWide-style arms instead.
- Rebuild cleanly with all wasmtime crates at one version to rule out enum/type mismatches.
- Report a minimal .wasm repro to the Wasmtime maintainers if it occurs on an unmodified release.
Example fix
// before
let (min_value, neg_one_value) = if int_type == self.intrinsics.i32_ty {
(...)
} else if int_type == self.intrinsics.i64_ty {
(...)
} else {
unreachable!()
};
// after: handle i128 explicitly (or reject it earlier with a proper error)
} else if int_type == self.intrinsics.i128_ty {
let min_value = int_type.const_int(i128::MIN as u128, false);
let neg_one_value = int_type.const_int((-1i128) as u128, false);
(min_value, neg_one_value)
} else {
unreachable!()
}; Defensive patterns
Strategy: validation
Validate before calling
// Ensure only integer types the translator expects reach div/rem lowering;
// at the API level, validate the module and reject experimental numeric opcodes.
let features = wasmparser::WasmFeatures::default(); // no wide-arithmetic on stock releases
wasmparser::Validator::new_with_features(features)
.validate_all(&wasm_bytes)
.map_err(|e| CompileError::InvalidModule(e))?; Type guard
fn is_compiler_panic(err: &anyhow::Error) -> bool {
err.to_string().contains("entered unreachable code")
} Try / catch
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| Module::new(&engine, &wasm_bytes))); result.map_err(|p| CompilerCrash::from_panic(p))?
Prevention
- Enable the wide-arithmetic Cargo feature (or upgrade) before compiling modules that use i128-wide ops.
- Don't hand-patch the div/rem overflow-trap path; extend type coverage explicitly.
- Pin wasmtime crates to one release and rebuild cleanly after upgrades.
- Validate modules with a feature set matching your compiler build.
When it happens
Trigger: Compiling a Wasm `i32.rem_s`/`i64.rem_s`-style operator (div/rem with overflow trap) when the value on the stack has an unexpected LLVM integer type — only possible through an internal bug: a new numeric proposal (e.g. wide-arithmetic i128) routed into the scalar div/rem path, or a refactoring that changed the type of the pushed operand.
Common situations: Running a nightly/fork of wasmtime with partial wide-arithmetic support and a module that does `i128`-wide arithmetic; fuzzing the compiler with hand-built modules; mixing incompatible wasmtime subcrate versions so operator-to-type assumptions drift.
Related errors
- duplicate function
- Can't form ExtraInfo with two pending canonicalization
- Internal error: resolution record for symbol {name} indicate
- Internal error: function table index {index} already occupie
- Internal error: table base out of sync with linker state
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/27ed7161713d0b68.
Report an issue: GitHub.