kovidgoyal/kitty · error · InvalidMods

Invalid shortcut

Error message

Invalid shortcut

What it means

parse_shortcut raises InvalidMods('Invalid shortcut') when a key spec contains modifier components but parse_mods returns 0, i.e. no valid modifier names (shift/ctrl/alt/super) were found before the '+'. This is a subclass of ValueError used during parse_map processing of keyboard shortcuts.

Source

Thrown at kitty/options/utils.py:580

        if src & (src - 1):
            bad('the source must name exactly one modifier')
            return {}
        if src == dest:
            bad('the source and destination are the same')
            return {}
        ans[src] = dest
    return ans


def parse_shortcut(sc: str) -> SingleKey:
    if sc.endswith('+') and len(sc) > 1:
        sc = f'{sc[:-1]}plus'
    parts = sc.split('+')
    mods = 0
    if len(parts) > 1:
        mods = parse_mods(parts[:-1], sc) or 0
        if not mods:
            raise InvalidMods('Invalid shortcut')
    q = parts[-1]
    q = character_key_name_aliases_with_ascii_lowercase.get(q.upper(), q)
    is_native = False
    if q.startswith('0x'):
        try:
            key = int(q, 16)
        except Exception:
            key = 0
        else:
            is_native = True
    else:
        try:
            key = ord(q)
        except Exception:
            uq = q.upper()
            uq = functional_key_name_aliases.get(uq, uq)
            x: int | None = getattr(defines, f'GLFW_FKEY_{uq}', None)
            if x is None:

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Use correct modifier names: ctrl, alt, shift, super (or cmd/meta aliases)
  2. Remove stray plus signs; a literal '+' key is written as 'plus'
  3. Check the map action syntax in kitty docs

Example fix

# before
map control+x copy_to_clipboard
# after
map ctrl+x copy_to_clipboard
Defensive patterns

Strategy: validation

Validate before calling

MODS = {'ctrl','shift','alt','super','meta','cmd','control'}
def valid_shortcut(sc: str) -> bool:
    parts = sc.split('+')
    return all(p.lower() in MODS for p in parts[:-1]) and bool(parts[-1])

Try / catch

try:
    parse_shortcut(sc)
except ValueError:
    skip_binding(sc)

Prevention

When it happens

Trigger: map lines like map ctrl+foo echo hi where 'foo' before the final segment is not a modifier; e.g. map caps+x ... or stray '+' patterns like map +x ...

Common situations: Typos in modifiers (control vs ctrl, command vs super on non-mac docs), keyboard layout guides using wrong modifier names, extra '+' characters.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/03f8becb646f97c9. Report an issue: GitHub.