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

  1. 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.
  2. Single characters (letters, digits, punctuation other than '-') can be used directly, e.g. "C-x".
  3. 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

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


AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16). Data as JSON: /api/errors/d2283a2f7a1ddfe0. Report an issue: GitHub.