zellij-org/zellij · error

Invalid KeyModifier value: {}

Error message

Invalid KeyModifier value: {}

What it means

Thrown by key_modifier_from_proto_i32 (zellij-utils/src/ipc/enum_conversions.rs) when ProtoKeyModifier::from_i32 returns None: a key_modifiers entry carries an i32 that is not a valid KeyModifier discriminant. Mirror of [76] for the modifier enum; in-range-but-unset values are reported as [75] instead.

Source

Thrown at zellij-utils/src/ipc/enum_conversions.rs:137

// Helper functions for converting between protobuf i32 and enum types
pub fn bare_key_to_proto_i32(key: BareKey) -> i32 {
    ProtoBareKey::from(key) as i32
}

pub fn bare_key_from_proto_i32(value: i32) -> Result<BareKey> {
    let proto_key =
        ProtoBareKey::from_i32(value).ok_or_else(|| anyhow!("Invalid BareKey value: {}", value))?;
    proto_key.try_into()
}

pub fn key_modifier_to_proto_i32(modifier: KeyModifier) -> i32 {
    ProtoKeyModifier::from(modifier) as i32
}

pub fn key_modifier_from_proto_i32(value: i32) -> Result<KeyModifier> {
    let proto_modifier = ProtoKeyModifier::from_i32(value)
        .ok_or_else(|| anyhow!("Invalid KeyModifier value: {}", value))?;
    proto_modifier.try_into()
}

View on GitHub (pinned to 5cb5df5cce)

Solutions

  1. Use matching zellij releases on both IPC endpoints.
  2. Emit modifiers only via key_modifier_to_proto_i32 / `KeyModifier::X as i32`.
  3. Guard inbound lists: validate each i32 with ProtoKeyModifier::from_i32(...).is_some() and reject the frame otherwise.

Example fix

// before
let mods: Result<Vec<_>> = raw_mods.iter().map(|m| key_modifier_from_proto_i32(*m)).collect(); // Err("Invalid KeyModifier value: 42")

// after: validate before converting
let mods: Vec<KeyModifier> = raw_mods.iter()
    .filter(|m| ProtoKeyModifier::from_i32(**m).is_some())
    .map(|m| key_modifier_from_proto_i32(*m).unwrap())
    .collect::<Result<_>>()?;
Defensive patterns

Strategy: validation

Validate before calling

// validate each modifier discriminant before conversion
let mods: Result<Vec<KeyModifier>> = raw_mods
    .into_iter()
    .filter(|m| ProtoKeyModifier::from_i32(*m).is_some())
    .map(key_modifier_from_proto_i32)
    .collect();

Type guard

fn is_known_modifier(v: i32) -> bool {
    ProtoKeyModifier::from_i32(v).is_some()
}

Try / catch

match key_modifier_from_proto_i32(value) {
    Ok(m) => Some(m),
    Err(e) if e.to_string().starts_with("Invalid KeyModifier value") => {
        log::warn!("unknown KeyModifier discriminant {value}; entry skipped");
        None
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A modifier i32 outside the Ctrl/Alt/Shift/Super/Unspecified range — newer client sending a modifier an older server lacks, handcrafted frames with arbitrary numbers, or arithmetic producing invalid discriminants.

Common situations: Mixed zellij versions between client and server; custom clients guessing numeric modifier codes; fuzzed or corrupted IPC input.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of zellij-org/zellij@5cb5df5cce (2026-08-19). Data as JSON: /api/errors/3164fbbd3ba25389. Report an issue: GitHub.