linera-io/linera-protocol · error
Invalid `i32` stored in `i64`
Error message
Invalid `i32` stored in `i64`
What it means
linera-witty maps WIT primitive types onto WebAssembly flat types per the Component Model canonical ABI. When a value was 'joined' into an i64 flat slot (e.g. flattening a variant whose cases have different widths) and must be split back into an i32-backed type, `i64::try_into::<i32>()` fails for values outside [-2^31, 2^31-1]. The panic means the guest returned a flat value that does not fit the declared WIT type — a guest-side ABI contract violation, not normal control flow.
Source
Thrown at linera-witty/src/primitive_types/flat_type.rs:65
fn split_from_f64(joined_f64: f64) -> Self;
/// Splits off the `Target` flat type from this flat type.
///
/// # Panics
///
/// If the `Target` type can't be joined into this flat type.
fn split_into<Target: FlatType>(self) -> Target;
}
impl FlatType for i32 {
fn split_from_i32(joined_i32: i32) -> Self {
joined_i32
}
fn split_from_i64(joined_i64: i64) -> Self {
joined_i64
.try_into()
.expect("Invalid `i32` stored in `i64`")
}
fn split_from_f32(_joined_f32: f32) -> Self {
unreachable!("`i32` is never joined into `f32`");
}
fn split_from_f64(_joined_f64: f64) -> Self {
unreachable!("`i32` is never joined into `f64`");
}
fn split_into<Target: FlatType>(self) -> Target {
Target::split_from_i32(self)
}
}
impl FlatType for i64 {
fn split_from_i32(_joined_i32: i32) -> Self {
unreachable!("`i64` is never joined into `i32`");View on GitHub (pinned to 6c226ddcb3)
Solutions
- Range-check in the guest before returning any i32-backed WIT type (u32/s32/char/bool): return an error variant instead of an out-of-range value
- Regenerate and rebuild guest and host from the same WIT package version so the flat layouts match
- If values legitimately exceed i32, change the WIT type to u64/s64 and regenerate both sides
- Add a boundary unit test asserting every returned value fits its declared WIT type
Example fix
// before (guest): computes in i64, returns unchecked across the WIT boundary
fn score(delta: u64) -> u32 { big_computation(delta) as u32 } // joined i64 > i32::MAX -> host panics on split
// after (guest): validate at the boundary
fn score(delta: u64) -> Result<u32, Error> {
let v = big_computation(delta);
u32::try_from(v).map_err(|_| Error::Overflow)
} Defensive patterns
Strategy: type-guard
Validate before calling
// guest: guard every i32-backed return before crossing the WIT boundary
let v: i64 = compute();
assert!(i32::try_from(v).is_ok(), "value {v} overflows the declared WIT type"); Type guard
fn fits_i32(v: i64) -> bool { i32::try_from(v).is_ok() } Prevention
- Regenerate host and guest bindings together whenever the WIT interface changes
- Keep integer widths identical (WIT u32 <-> Rust u32) on both sides of the boundary
- Never cast wide-to-narrow at the boundary without try_from
When it happens
Trigger: A guest function whose result unflattens through an i64-to-i32 split returns a value such as 3_000_000_000; guest and host compiled from different WIT interfaces, so an i64-wide value is read where an i32-backed type (u32/s32/char) is declared.
Common situations: A WIT type changed from u32 to u64 (or vice versa) and only one side regenerated; a guest computing in i64 and returning across the boundary without a range check; fuzzing guests with unchecked arithmetic.
Related errors
- Attempt to unflatten an invalid `char`
- A Wasm runtime is required to load user applications. Please
- BytecodeTooLarge
- MaximumFuelExceeded
- ReentrantCall
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/c4e62e6d59db008d.
Report an issue: GitHub.