fathyb/carbonyl · error

Unexpected mask value

Error message

Unexpected mask value

What it means

This panic comes from `FourBits::new`, which packs four booleans into a 4-bit value and maps it to a `FourBits` enum variant. Because four bools can only produce 0b0000..0b1111, the catch-all `_ => panic!("Unexpected mask value")` branch is logically unreachable — the Rust exhaustiveness checker can't prove that, so a defensive panic arm is required. It signals an internal invariant violation (e.g. corrupted match input), not user-callable invalid input.

Source

Thrown at src/utils/four_bits.rs:41

        match (x as u8) << 3 | (y as u8) << 2 | (z as u8) << 1 | (w as u8) << 0 {
            0b0000 => B0000,
            0b0001 => B0001,
            0b0010 => B0010,
            0b0011 => B0011,
            0b0100 => B0100,
            0b0101 => B0101,
            0b0110 => B0110,
            0b0111 => B0111,
            0b1000 => B1000,
            0b1001 => B1001,
            0b1010 => B1010,
            0b1011 => B1011,
            0b1100 => B1100,
            0b1101 => B1101,
            0b1110 => B1110,
            0b1111 => B1111,
            _ => panic!("Unexpected mask value"),
        }
    }
}

View on GitHub (pinned to ab80a276b1)

Solutions

  1. Verify the bit-packing expression shifts each bool into its own bit: (x as u8) << 3 | (y as u8) << 2 | (z as u8) << 1 | (w as u8).
  2. If inputs exceed four bits, widen the enum or split into multiple FourBits values instead of hitting the panic arm.
  3. Replace the panic with `unreachable!("Unexpected mask value")` or, better, make `new` return Result/self-documenting via TryFrom<u8> if arbitrary u8 input must be accepted.
  4. If the panic fires at runtime, add a debug_assert!/log of the computed mask value to identify the corrupted input path.

Example fix

// before
_ => panic!("Unexpected mask value"),

// after
_ => unreachable!("mask is built from 4 bools, value must be 0..=15"),
Defensive patterns

Strategy: validation

Validate before calling

fn mask_from(x: bool, y: bool, z: bool, w: bool) -> u8 {
    let m = (x as u8) << 3 | (y as u8) << 2 | (z as u8) << 1 | w as u8;
    debug_assert!(m <= 0b1111, "mask {m} exceeds 4 bits");
    m
}

Type guard

fn is_four_bit(v: u8) -> bool { v <= 0b1111 }

Try / catch

// panics cannot be caught in Rust; instead use catch_unwind if interfacing with FFI
let result = std::panic::catch_unwind(|| FourBits::new(x, y, z, w));
match result {
    Ok(bits) => /* use bits */,
    Err(_) => /* handle panic: log and fall back */,
}

Prevention

When it happens

Trigger: Only reachable if the bit-packing expression in `FourBits::new` (src/utils/four_bits.rs:24) is modified to produce values outside 0b0000..0b1111 (e.g. shifting by wrong amounts, adding a fifth input, or using non-boolean-cast arithmetic). Calling `FourBits::new(x, y, z, w)` with any combination of four bools can never trigger it in the current code.

Common situations: Developers hit this after editing `new` (changed shift offsets, swapped `|` for `|=` on a wider type, extra inputs beyond 4 bits), or when refactoring the enum/mask types so the match receives a wider `u8`. It can also appear in coverage reports as an uncovered branch.


AI-assisted analysis of fathyb/carbonyl@ab80a276b1 (2026-09-02). Data as JSON: /api/errors/d6e4d9b833b91bfd. Report an issue: GitHub.