gleam-lang/gleam · error

bytes must not be empty

Error message

bytes must not be empty

What it means

Inside the bit-width conversion for bit-array pattern/value matching, the code left-shifts the last byte of a byte vector to drop excess bits when the bit size isn't a multiple of 8. `bytes.last_mut().expect("bytes must not be empty")` panics if the byte vector is empty, i.e. a zero-byte BitVec was passed in. The invariant is that a bit array with a non-zero size in bits has at least one byte.

Solutions

  1. Ensure the bytes conversion produces at least one byte for any non-zero bit size before calling this code
  2. Short-circuit: if `bytes.is_empty()`, return `BitVec::new()` (or `BitVec::from_vec(bytes)`) instead of shifting
  3. Validate that segment `size > 0` and that `size`/8 == bytes.len() (or len+1) upstream
  4. If reproducible with valid Gleam source, file a compiler bug with the bit-array expression

Example fix

// before
let last_byte = bytes.last_mut().expect("bytes must not be empty");
*last_byte <<= 8 - remainder;
// after
if bytes.is_empty() {
    return BitVec::new();
}
*bytes.last_mut().unwrap() <<= 8 - remainder;
Defensive patterns

Strategy: validation

Validate before calling

if size == 0 || bytes.is_empty() {
    return BitVec::new();
}

Type guard

fn convertible(bytes: &[u8], size: usize) -> bool { !bytes.is_empty() && size > 0 && size <= bytes.len() * 8 }

Prevention

When it happens

Trigger: Calling the bit-array size/truncation helper with `bytes` empty while `remainder != 0` — e.g. size in bits > 0 but the byte conversion produced no bytes, or a zero-sized bit array segment whose remainder computation is inconsistent.

Common situations: Hit during Gleam compiler work on bit-array pattern matching: passing an empty `Vec<u8>` from an integer-to-bytes conversion for a zero value with a small segment size, or a bug in segment size validation.

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/087d60498e2d69b3. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/exhaustiveness.rs:4061

        }
        // If the number needs fewer bits than the read size, we pad it.
        // <3:9> yields the bytes [0b0000_0011]. We add one digit and get 0_0000_0011
        (Endianness::Big, Ordering::Less) => {
            let mut bits = BitVec::repeat(pad_digit, size - bytes_size);
            bits.extend_from_raw_slice(&bytes);
            bits
        }

        (Endianness::Little, Ordering::Greater) => {
            // If the difference is greater than a byte, we returned an Error earlier
            let remainder = size % 8;
            if remainder == 0 {
                BitVec::from_vec(bytes)
            } else {
                // If the size is not a multiple of 8, we need to truncate the most significant bits.
                // As they are in the last byte, we leftshift by the appropriate amount and
                // truncate the final bits after conversion
                let last_byte = bytes.last_mut().expect("bytes must not be empty");
                *last_byte <<= 8 - remainder;

                let mut bits = BitVec::from_vec(bytes);
                bits.truncate(size);
                bits
            }
        }
        (Endianness::Little, Ordering::Less) => {
            let mut bits = BitVec::from_vec(bytes);
            let padding: BitVec<u8, Msb0> = BitVec::repeat(pad_digit, size - bytes_size);
            bits.extend_from_bitslice(padding.as_bitslice());
            bits
        }
    };
    Ok(bits)
}

fn int_to_bytes(value: &BigInt, endianness: Endianness, signed: bool) -> Vec<u8> {

View on GitHub (pinned to 49f8762da5)