{"record":{"id":"27f23dea1171f46c","repo":"GitoxideLabs/gitoxide","slug":"command-shortcuts-always-contain-a-leaf-key","errorCode":null,"errorMessage":"command shortcuts always contain a leaf key","messagePattern":"command shortcuts always contain a leaf key","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/command_menu.rs","lineNumber":101,"sourceCode":"}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub(crate) struct Command {\n    pub(crate) id: CommandId,\n    pub(crate) group: CommandGroup,\n    pub(crate) row: usize,\n    pub(crate) label: &'static str,\n    pub(crate) shortcut: &'static str,\n    pub(crate) active: bool,\n    pub(crate) action: Action,\n}\n\nimpl Command {\n    pub(crate) fn key(&self) -> char {\n        self.shortcut\n            .chars()\n            .next_back()\n            .expect(\"command shortcuts always contain a leaf key\")\n    }\n}\n\npub(crate) fn commands(app: &App, decorations: &Decorations, has_verifiable_signatures: bool) -> Vec<Command> {\n    let mut out = Vec::with_capacity(35);\n    let mut push = |id, group, row, label, shortcut, active, action| {\n        out.push(Command {\n            id,\n            group,\n            row,\n            label,\n            shortcut,\n            active,\n            action,\n        });\n    };\n\n    let (date_label, date_active) = match app.date_mode {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/command_menu.rs#L83-L119","documentation":"This is a Rust `expect` panic in `Command::key()` (gix-tix/src/command_menu.rs:101). The code derives a command's keybinding by taking the last character (`chars().next_back()`) of the human-readable shortcut string. The library authors assert that every command's shortcut contains at least one 'leaf' key character; the panic fires when a shortcut string is empty.","triggerScenarios":"Calling `Command::key()` on a `Command` whose `shortcut` field is an empty string. This happens if a command is constructed with `shortcut: \"\"` in `commands()` in gix-tix/src/command_menu.rs, e.g. via the `push` closure, or if a shortcut is built dynamically (e.g. from config or locale data) and ends up empty.","commonSituations":"Developers adding a new menu command and forgetting to fill in the shortcut, or building shortcuts programmatically from translated/config-driven key names that resolve to an empty string. Since `next_back()` returns `Option<char>`, the crash surfaces only when the accessor runs, not at construction.","solutions":["Set a non-empty `shortcut` for every command pushed in `commands()`; never pass an empty string literal.","Validate at construction time: make `Command::new` reject or fall back on empty shortcuts so the invariant fails fast with a clear message.","If shortcuts can come from external data, guard the accessor: `self.shortcut.chars().next_back().unwrap_or('\\0')` or return `Option<char>` and handle the None case in the caller."],"exampleFix":"// before\npub(crate) fn key(&self) -> char {\n    self.shortcut.chars().next_back().expect(\"command shortcuts always contain a leaf key\")\n}\n// after\npub(crate) fn key(&self) -> Option<char> {\n    self.shortcut.chars().next_back()\n}\n// or keep the signature and validate at the construction site:\n// assert!(!shortcut.is_empty(), \"command shortcut must have a leaf key\");","handlingStrategy":"validation","validationCode":"// Before registering/using a Command:\nfn valid_command(c: &Command) -> bool {\n    !c.shortcut.is_empty() && c.shortcut.chars().next_back().is_some()\n}\nassert!(valid_command(&cmd), \"command {:?} has an empty shortcut\", cmd.id);","typeGuard":"fn leaf_key(c: &Command) -> Option<char> {\n    c.shortcut.chars().next_back()\n}","tryCatchPattern":null,"preventionTips":["Never construct `Command` with an empty shortcut; use a compile-time constant or a constructor that validates.","Add a unit test asserting every command from `commands()` has a non-empty shortcut.","If shortcuts come from config/translations, validate them at load time."],"tags":["panic","empty-string","keybinding","invariant"],"backgroundTag":"empty-required-field","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}