{"record":{"id":"cc8e055ba5fa43be","repo":"zellij-org/zellij","slug":"character-key-missing-character-data","errorCode":null,"errorMessage":"Character key missing character data","messagePattern":"Character key missing character data","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"zellij-utils/src/ipc/protobuf_conversion.rs","lineNumber":3161,"sourceCode":"}\n\nimpl TryFrom<crate::client_server_contract::client_server_contract::KeyWithModifier>\n    for crate::data::KeyWithModifier\n{\n    type Error = anyhow::Error;\n    fn try_from(\n        key: crate::client_server_contract::client_server_contract::KeyWithModifier,\n    ) -> Result<Self> {\n        use crate::ipc::enum_conversions::{bare_key_from_proto_i32, key_modifier_from_proto_i32};\n        use std::collections::BTreeSet;\n\n        // Handle character keys specially\n        let bare_key = if key.bare_key\n            == crate::client_server_contract::client_server_contract::BareKey::Char as i32\n        {\n            let character_str = key\n                .character\n                .ok_or_else(|| anyhow!(\"Character key missing character data\"))?;\n            let character = character_str\n                .chars()\n                .next()\n                .ok_or_else(|| anyhow!(\"Empty character string\"))?;\n            crate::data::BareKey::Char(character)\n        } else {\n            bare_key_from_proto_i32(key.bare_key)?\n        };\n\n        let key_modifiers: Result<BTreeSet<_>> = key\n            .key_modifiers\n            .into_iter()\n            .map(|modifier| key_modifier_from_proto_i32(modifier))\n            .collect();\n\n        Ok(Self {\n            bare_key,\n            key_modifiers: key_modifiers?,","sourceCodeStart":3143,"sourceCodeEnd":3179,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-utils/src/ipc/protobuf_conversion.rs#L3143-L3179","documentation":"When converting a protobuf KeyWithModifier to zellij's data::Key, the special case bare_key == BareKey::Char means the actual character must arrive in the separate optional string field character (proto comment: 'Only set when bare_key is CHAR'). If the discriminator says Char but character is None, the conversion fails with 'Character key missing character data'. The proto enum has Unspecified = 0, so this is not a default-value trap - the sender explicitly chose Char without the payload.","triggerScenarios":"Sending a KeyWithModifier over IPC with bare_key = BareKey::Char as i32 but character unset, e.g. serializing a key event and dropping the character string, or a keymap tool that encodes every printable key as Char without attaching the char.","commonSituations":"Custom input pipelines or remote/protocol clients forwarding key events into zellij; plugins synthesizing key events; version skew where an older sender's KeyWithModifier had no character field.","solutions":["Always pair bare_key = BareKey::Char with character: Some(\"a\".to_string()) containing the character.","If the key is not a printable character, send the matching named BareKey variant (Enter, Tab, Esc, ...) instead of Char.","Write a round-trip test data::Key -> KeyWithModifier -> data::Key over your key set.","Rebuild senders against the current contract so the character field exists."],"exampleFix":"// before\nlet key = KeyWithModifier {\n    bare_key: BareKey::Char as i32,\n    key_modifiers: vec![KeyModifier::Ctrl as i32],\n    character: None, // -> \"Character key missing character data\"\n};\n\n// after\nlet key = KeyWithModifier {\n    bare_key: BareKey::Char as i32,\n    key_modifiers: vec![KeyModifier::Ctrl as i32],\n    character: Some(\"c\".to_string()),\n};","handlingStrategy":"validation","validationCode":"fn key_is_convertible(k: &KeyWithModifier) -> bool {\n    if k.bare_key == BareKey::Char as i32 {\n        matches!(k.character.as_deref(), Some(s) if !s.is_empty())\n    } else {\n        true // named keys validated by bare_key_from_proto_i32\n    }\n}","typeGuard":"fn is_valid_char_key(k: &KeyWithModifier) -> bool {\n    k.bare_key != BareKey::Char as i32\n        || k.character\n            .as_deref()\n            .map(|s| s.chars().next().is_some())\n            .unwrap_or(false)\n}","tryCatchPattern":"match zellij_utils::data::Key::try_from(proto_key) {\n    Ok(key) => handle_key(key),\n    Err(e) if e.to_string().contains(\"Character key missing character data\") => {\n        log::warn!(\"dropping Char key without character payload\");\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Treat bare_key = Char and the character string as an inseparable pair when serializing key events.","Only set bare_key = Char for printable characters; use named BareKey variants otherwise.","Round-trip test your full keymap through the proto layer."],"tags":["zellij","ipc","protobuf","rust","keyboard","input"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}