{"record":{"id":"fa312bf8814379c8","repo":"helix-editor/helix","slug":"invalid-function-key","errorCode":null,"errorMessage":"Invalid function key '{}'","messagePattern":"Invalid function key '(.+?)'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"helix-view/src/input.rs","lineNumber":397,"sourceCode":"            keys::LEFT_ALT => KeyCode::Modifier(ModifierKeyCode::LeftAlt),\n            keys::LEFT_SUPER => KeyCode::Modifier(ModifierKeyCode::LeftSuper),\n            keys::LEFT_HYPER => KeyCode::Modifier(ModifierKeyCode::LeftHyper),\n            keys::LEFT_META => KeyCode::Modifier(ModifierKeyCode::LeftMeta),\n            keys::RIGHT_SHIFT => KeyCode::Modifier(ModifierKeyCode::RightShift),\n            keys::RIGHT_CONTROL => KeyCode::Modifier(ModifierKeyCode::RightControl),\n            keys::RIGHT_ALT => KeyCode::Modifier(ModifierKeyCode::RightAlt),\n            keys::RIGHT_SUPER => KeyCode::Modifier(ModifierKeyCode::RightSuper),\n            keys::RIGHT_HYPER => KeyCode::Modifier(ModifierKeyCode::RightHyper),\n            keys::RIGHT_META => KeyCode::Modifier(ModifierKeyCode::RightMeta),\n            keys::ISO_LEVEL_3_SHIFT => KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift),\n            keys::ISO_LEVEL_5_SHIFT => KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift),\n            single if single.chars().count() == 1 => KeyCode::Char(single.chars().next().unwrap()),\n            function if function.len() > 1 && function.starts_with('F') => {\n                let function: String = function.chars().skip(1).collect();\n                let function = str::parse::<u8>(&function)?;\n                (function > 0 && function < 25)\n                    .then_some(KeyCode::F(function))\n                    .ok_or_else(|| anyhow!(\"Invalid function key '{}'\", function))?\n            }\n            // Checking that the last token is empty ensures that this branch is only taken if\n            // `-` is used as a code. For example this branch will not be taken for `S-` (which is\n            // missing a code).\n            _ if s.ends_with('-') && tokens.last().is_some_and(|t| t.is_empty()) => {\n                if s == \"-\" {\n                    return Ok(KeyEvent {\n                        code: KeyCode::Char('-'),\n                        modifiers: KeyModifiers::empty(),\n                    });\n                } else {\n                    let suggestion = format!(\"{}-{}\", s.trim_end_matches('-'), keys::MINUS);\n                    return Err(anyhow!(\n                        \"Key '-' cannot be used with modifiers, use '{}' instead\",\n                        suggestion\n                    ));\n                }\n            }","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/helix-editor/helix/blob/079a789e8cb08ead67f19e1971a1b7438b37354b/helix-view/src/input.rs#L379-L415","documentation":"In KeyEvent::from_str, a token longer than one char starting with 'F' is treated as a function key: the digits after F are parsed as u8 and must satisfy 1..=24 (function > 0 && function < 25). Non-numeric digits surface the u8 parse error; out-of-range numbers (0 or >= 25) fail with \"Invalid function key '{n}'\", where the message shows the parsed number, not the original string.","triggerScenarios":"Keymap entries like \"F0\", \"F25\", \"F99\" (beyond crossterm's F1-F24 set), or \"F1x\"/\"Fabc\" (non-numeric suffix, which instead surfaces the integer parse error).","commonSituations":"Migrating configs from terminals/emulators with more function keys (some support F25+); expecting media keys to be F-keys; trailing characters after the number from copy-paste.","solutions":["Use only F1 through F24.","For keys above F12, confirm your terminal actually encodes them (crossterm must recognize the escape sequence) before binding.","Remove stray characters after the digits; \"F1\" not \"F1 \"."],"exampleFix":"# before (config.toml)\n \"F25\" = \"workspace_command_picker\" # Err: Invalid function key '25'\n\n# after\n \"F13\" = \"workspace_command_picker\" # F1-F24 only","handlingStrategy":"validation","validationCode":"fn valid_function_key(spec: &str) -> bool {\n    if let Some(num) = spec.strip_prefix('F') {\n        matches!(num.parse::<u8>(), Ok(n) if (1..=24).contains(&n))\n    } else {\n        true\n    }\n}","typeGuard":"fn parse_function_key(spec: &str) -> Option<crossterm::event::KeyCode> {\n    let num = spec.strip_prefix('F')?;\n    let n = num.parse::<u8>().ok()?;\n    (1..=24).contains(&n).then(|| crossterm::event::KeyCode::F(n))\n}","tryCatchPattern":"let ev = match spec.parse::<KeyEvent>() {\n    Ok(ev) => ev,\n    Err(err) if err.to_string().contains(\"Invalid function key\") => {\n        return Err(anyhow!(\"'{spec}': only F1-F24 are supported\"));\n    }\n    Err(err) => return Err(err),\n};","preventionTips":["Clamp bindings to F1-F24; crossterm defines no higher function keys.","Validate generated keymaps programmatically before shipping config.","Remember the error prints the number only, not the original string."],"tags":["keymap","input","helix","rust"],"backgroundTag":null,"analyzedSha":"079a789e8cb08ead67f19e1971a1b7438b37354b","analyzedAt":"2026-08-16T09:09:59.668Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}