helix-editor/helix · error · anyhow::Error
Invalid key code '{}'
Error message
Invalid key code '{}' What it means
This is the catch-all branch of KeyEvent::from_str: the final token matched none of the named keys (backspace, ret, left, ..., esc, space, minus, lt, gt, media/modifier keys), is not a single character, and is not an F-key pattern. The offending token is echoed so you can see exactly what failed to match.
Source
Thrown at helix-view/src/input.rs:416
}
// Checking that the last token is empty ensures that this branch is only taken if
// `-` is used as a code. For example this branch will not be taken for `S-` (which is
// missing a code).
_ if s.ends_with('-') && tokens.last().is_some_and(|t| t.is_empty()) => {
if s == "-" {
return Ok(KeyEvent {
code: KeyCode::Char('-'),
modifiers: KeyModifiers::empty(),
});
} else {
let suggestion = format!("{}-{}", s.trim_end_matches('-'), keys::MINUS);
return Err(anyhow!(
"Key '-' cannot be used with modifiers, use '{}' instead",
suggestion
));
}
}
invalid => return Err(anyhow!("Invalid key code '{}'", invalid)),
};
let mut modifiers = KeyModifiers::empty();
for token in tokens {
let flag = match token {
"S" => KeyModifiers::SHIFT,
"A" => KeyModifiers::ALT,
"C" => KeyModifiers::CONTROL,
"Meta" | "Cmd" | "Win" => KeyModifiers::SUPER,
_ => return Err(anyhow!("Invalid key modifier '{}-'", token)),
};
if modifiers.contains(flag) {
return Err(anyhow!("Repeated key modifier '{}-'", token));
}
modifiers.insert(flag);
}
View on GitHub (pinned to 079a789e8c)
Solutions
- Use Helix's key names: ret, esc, space, tab, del, ins, backspace, home, end, pageup, pagedown, up, down, left, right, minus, lt, gt, capslock, numlock, F1-F24, etc.
- Single characters (letters, digits, punctuation other than '-') can be used directly, e.g. "C-x".
- Check for trailing spaces or smart quotes around the token in the config file.
Example fix
# before (config.toml) "C-return" = "code_action" # after "C-ret" = "code_action"
Defensive patterns
Strategy: validation
Validate before calling
const KEY_NAMES: &[&str] = &["backspace","ret","left","right","up","down","home","end",
"pageup","pagedown","tab","del","ins","null","esc","space","minus","lt","gt",
"capslock","scrolllock","numlock","printscreen","pause","menu","keypadbegin"];
fn valid_key_code(tok: &str) -> bool {
KEY_NAMES.contains(&tok)
|| tok.chars().count() == 1
|| (tok.starts_with('F') && (1..=24).contains(&tok[1..].parse::<u8>().unwrap_or(0)))
} Type guard
fn parse_key_code(tok: &str) -> Option<crossterm::event::KeyCode> {
KeyEvent::from_str(&format!("{tok}")).ok().map(|ev| ev.code)
} Try / catch
let ev = spec.parse::<KeyEvent>()
.map_err(|e| anyhow!("keymap entry '{spec}': {e} — see helix-view/src/input.rs keys module for names"))?; Prevention
- Use Helix's short names: ret, esc, del, ins, space, not return/escape/delete/insert.
- Lint keymaps by parsing every key string at config load.
- Avoid trailing whitespace and smart quotes when editing config files.
When it happens
Trigger: Keymap entries with unrecognized key names: "C-return" (correct name is "ret"), "C-escape" (correct is "esc"), "C-delete" (correct is "del"), multi-char strings like "C-ab", or typos like "spce".
Common situations: Guessing long-form key names (return/escape/delete/insert) when Helix uses the short forms ret/esc/del/ins; porting keymaps from other editors whose naming differs; invisible whitespace copied into the config.
Related errors
- Missing key code
- Invalid function key '{}'
- Key '-' cannot be used with modifiers, use '{}' instead
- Invalid key modifier '{}-'
- Repeated key modifier '{}-'
AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16).
Data as JSON: /api/errors/d2283a2f7a1ddfe0.
Report an issue: GitHub.