gleam-lang/gleam · error
trailing_zeros to return a value for non-zero numbers
Error message
trailing_zeros to return a value for non-zero numbers
What it means
When computing the required bit width for a negative integer pattern (adding sign bits), the code calls `trailing_zeros().expect(...)` which panics if the BigInt is zero — `trailing_zeros` returns None only for zero, but the comment says it is expected to be non-zero here. A zero value reaching this branch means the earlier zero/sign classification was wrong.
Solutions
- Fix the sign classification so zero is never classified as `Sign::Minus` (check `value == 0` before the match)
- Return the appropriate bit width directly for zero values before entering the negative branch
- Use `trailing_zeros().unwrap_or(bits_unsigned - 1)` style fallback only if zero can legitimately arrive
- Report a compiler bug with the literal that triggers it
Example fix
// before
let trailing_zeros = value.trailing_zeros().expect("trailing_zeros to return a value for non-zero numbers");
// after
let Some(trailing_zeros) = value.trailing_zeros() else {
return 1; // value is zero despite negative sign classification
}; Defensive patterns
Strategy: type-guard
Validate before calling
if value.sign() == Sign::NoSign {
return 1; // zero
} Type guard
let Some(trailing_zeros) = value.trailing_zeros() else { return fallback_width(value); }; Prevention
- Classify zero explicitly before the Sign::Minus branch
- Use exhaustive match on (sign, is_signed) including the zero case
- Property-test bit-width computation over negative, zero, and boundary BigInts
When it happens
Trigger: The exhaustiveness integer-range analysis classifies a value as `Sign::Minus` (negative) but the value is actually zero, so `trailing_zeros()` returns None and the expect panics.
Common situations: Seen when adding large/edge-case integer literals to bit-array or case pattern checks in Gleam; typically caused by a sign-detection bug (e.g. comparing magnitudes incorrectly) or a custom BigInt literal that round-trips to zero.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of gleam-lang/gleam@49f8762da5 (2026-09-14).
Data as JSON: /api/errors/51e3ef38b478204e.
Report an issue: GitHub.
Appendix: source
Thrown at compiler-core/src/exhaustiveness.rs:4243
return false;
}
let required_bits = match (value.sign(), signed) {
// Zero always needs one bit.
(Sign::NoSign, _) => 1,
// `BigInt::bits` does not consider the sign!
(Sign::Plus, false) => value.bits(),
// Therefore we need to add the sign bit here: `10` -> `010`
(Sign::Plus, true) => value.bits() + 1,
// A negative number must be signed
(Sign::Minus, false) => return false,
(Sign::Minus, true) => {
let bits_unsigned = value.bits();
let trailing_zeros = value
.trailing_zeros()
.expect("trailing_zeros to return a value for non-zero numbers");
let is_power_of_2 = trailing_zeros == bits_unsigned - 1;
// Negative powers of two don't need an extra sign bit. E.g. `-2 == 0b10`
if is_power_of_2 {
bits_unsigned
} else {
bits_unsigned + 1
}
}
};
match required_bits.to_u32() {
Some(required_bits) => bits >= required_bits,
None => false,
}
}
#[cfg(test)]View on GitHub (pinned to 49f8762da5)