zellij-org/zellij · error

Character key needs character data

Error message

Character key needs character data

What it means

Thrown by TryFrom<ProtoBareKey> for BareKey (zellij-utils/src/ipc/enum_conversions.rs): converting the bare enum value ProtoBareKey::Char can never succeed because the actual character is not part of the enum — it lives in the separate `character` field of the enclosing KeyWithModifier message, handled specially in protobuf_conversion.rs (3147-3152 on send, 3179-3192 on receive). Calling the enum-level conversion on a Char is therefore an API misuse.

Source

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

            ProtoBareKey::Right => Ok(BareKey::Right),
            ProtoBareKey::Home => Ok(BareKey::Home),
            ProtoBareKey::End => Ok(BareKey::End),
            ProtoBareKey::Backspace => Ok(BareKey::Backspace),
            ProtoBareKey::Delete => Ok(BareKey::Delete),
            ProtoBareKey::Insert => Ok(BareKey::Insert),
            ProtoBareKey::F1 => Ok(BareKey::F(1)),
            ProtoBareKey::F2 => Ok(BareKey::F(2)),
            ProtoBareKey::F3 => Ok(BareKey::F(3)),
            ProtoBareKey::F4 => Ok(BareKey::F(4)),
            ProtoBareKey::F5 => Ok(BareKey::F(5)),
            ProtoBareKey::F6 => Ok(BareKey::F(6)),
            ProtoBareKey::F7 => Ok(BareKey::F(7)),
            ProtoBareKey::F8 => Ok(BareKey::F(8)),
            ProtoBareKey::F9 => Ok(BareKey::F(9)),
            ProtoBareKey::F10 => Ok(BareKey::F(10)),
            ProtoBareKey::F11 => Ok(BareKey::F(11)),
            ProtoBareKey::F12 => Ok(BareKey::F(12)),
            ProtoBareKey::Char => Err(anyhow!("Character key needs character data")),
            ProtoBareKey::Tab => Ok(BareKey::Tab),
            ProtoBareKey::Esc => Ok(BareKey::Esc),
            ProtoBareKey::Enter => Ok(BareKey::Enter),
            ProtoBareKey::CapsLock => Ok(BareKey::CapsLock),
            ProtoBareKey::ScrollLock => Ok(BareKey::ScrollLock),
            ProtoBareKey::NumLock => Ok(BareKey::NumLock),
            ProtoBareKey::PrintScreen => Ok(BareKey::PrintScreen),
            ProtoBareKey::Pause => Ok(BareKey::Pause),
            ProtoBareKey::Menu => Ok(BareKey::Menu),
            ProtoBareKey::Unspecified => Err(anyhow!("Unspecified bare key")),
        }
    }
}

// KeyModifier conversions
impl From<KeyModifier> for ProtoKeyModifier {
    fn from(modifier: KeyModifier) -> Self {
        match modifier {

View on GitHub (pinned to bf8d23a4f7)

Solutions

  1. Convert the complete message: TryFrom<ProtoKeyWithModifier> for KeyWithModifier, which reads the sibling `character` field when bare_key == Char.
  2. When building the proto side, always set `character: Some(c.to_string())` together with bare_key = Char (mirror of the From impl at protobuf_conversion.rs:3148).
  3. If you must reject early, match on the discriminant and treat Char without character data as invalid input at the boundary.

Example fix

// before: enum-only conversion can never produce BareKey::Char
let key: BareKey = bare_key_from_proto_i32(proto.bare_key)?; // Err("Character key needs character data")

// after: convert the full message, which carries the character field
let key: KeyWithModifier = proto_key.try_into()?; // uses proto.character for Char
Defensive patterns

Strategy: type-guard

Validate before calling

// sender side: always pair Char with its character
let (bare, ch) = match key.bare_key {
    BareKey::Char(c) => (ProtoBareKey::Char as i32, Some(c.to_string())),
    other => (bare_key_to_proto_i32(other), None),
};
let msg = ProtoKeyWithModifier { bare_key: bare, key_modifiers: mods, character: ch };

Type guard

fn is_fully_specified_key(k: &ProtoKeyWithModifier) -> bool {
    if k.bare_key == ProtoBareKey::Char as i32 {
        k.character.as_deref().is_some_and(|s| !s.is_empty())
    } else {
        k.bare_key != ProtoBareKey::Unspecified as i32
    }
}

Try / catch

match proto_key.try_into::<KeyWithModifier>() {
    Ok(key) => Some(key),
    Err(e) if e.to_string().contains("Character key") => {
        log::warn!("dropping Char key without character data");
        None
    },
    Err(e) => { log::error!("key conversion failed: {e:#}"); None },
}

Prevention

When it happens

Trigger: Calling bare_key_from_proto_i32 (or TryFrom<ProtoBareKey>) with the Char discriminant instead of converting the whole KeyWithModifier message; or a handcrafted IPC frame that sets bare_key=Char but leaves `character` unset, then goes through a path that only converts the enum.

Common situations: Custom clients/web clients building key events field-by-field; plugin or test code reusing enum_conversions helpers directly; refactors that bypass the full-message conversion.

Related errors


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