helix-editor/helix · error · anyhow::Error

Invalid function key '{}'

Error message

Invalid function key '{}'

What it means

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.

Source

Thrown at helix-view/src/input.rs:397

            keys::LEFT_ALT => KeyCode::Modifier(ModifierKeyCode::LeftAlt),
            keys::LEFT_SUPER => KeyCode::Modifier(ModifierKeyCode::LeftSuper),
            keys::LEFT_HYPER => KeyCode::Modifier(ModifierKeyCode::LeftHyper),
            keys::LEFT_META => KeyCode::Modifier(ModifierKeyCode::LeftMeta),
            keys::RIGHT_SHIFT => KeyCode::Modifier(ModifierKeyCode::RightShift),
            keys::RIGHT_CONTROL => KeyCode::Modifier(ModifierKeyCode::RightControl),
            keys::RIGHT_ALT => KeyCode::Modifier(ModifierKeyCode::RightAlt),
            keys::RIGHT_SUPER => KeyCode::Modifier(ModifierKeyCode::RightSuper),
            keys::RIGHT_HYPER => KeyCode::Modifier(ModifierKeyCode::RightHyper),
            keys::RIGHT_META => KeyCode::Modifier(ModifierKeyCode::RightMeta),
            keys::ISO_LEVEL_3_SHIFT => KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift),
            keys::ISO_LEVEL_5_SHIFT => KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift),
            single if single.chars().count() == 1 => KeyCode::Char(single.chars().next().unwrap()),
            function if function.len() > 1 && function.starts_with('F') => {
                let function: String = function.chars().skip(1).collect();
                let function = str::parse::<u8>(&function)?;
                (function > 0 && function < 25)
                    .then_some(KeyCode::F(function))
                    .ok_or_else(|| anyhow!("Invalid function key '{}'", function))?
            }
            // 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
                    ));
                }
            }

View on GitHub (pinned to 079a789e8c)

Solutions

  1. Use only F1 through F24.
  2. For keys above F12, confirm your terminal actually encodes them (crossterm must recognize the escape sequence) before binding.
  3. Remove stray characters after the digits; "F1" not "F1 ".

Example fix

# before (config.toml)
 "F25" = "workspace_command_picker" # Err: Invalid function key '25'

# after
 "F13" = "workspace_command_picker" # F1-F24 only
Defensive patterns

Strategy: validation

Validate before calling

fn valid_function_key(spec: &str) -> bool {
    if let Some(num) = spec.strip_prefix('F') {
        matches!(num.parse::<u8>(), Ok(n) if (1..=24).contains(&n))
    } else {
        true
    }
}

Type guard

fn parse_function_key(spec: &str) -> Option<crossterm::event::KeyCode> {
    let num = spec.strip_prefix('F')?;
    let n = num.parse::<u8>().ok()?;
    (1..=24).contains(&n).then(|| crossterm::event::KeyCode::F(n))
}

Try / catch

let ev = match spec.parse::<KeyEvent>() {
    Ok(ev) => ev,
    Err(err) if err.to_string().contains("Invalid function key") => {
        return Err(anyhow!("'{spec}': only F1-F24 are supported"));
    }
    Err(err) => return Err(err),
};

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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