kovidgoyal/kitty · warning

Ignoring invalid remap_modifiers "{val}": {msg}

Error message

Ignoring invalid remap_modifiers "{val}": {msg}

What it means

The remap_modifiers option value failed validation; kitty logs the specific reason and disables the entire remapping (returns {}). Example reasons: not colon-separated pairs, unknown modifier names, or mapping a non-remappable modifier.

Source

Thrown at kitty/options/utils.py:541

def to_modifiers(val: str) -> int:
    return parse_mods(val.split('+'), val) or 0


def remap_modifiers(val: str) -> dict[int, int]:
    # Only the real modifiers may be remapped. parse_mods() also accepts
    # kitty_mod (a parse-time placeholder resolved out of keymaps before any key
    # event exists) and the lock modifiers, neither of which can be meaningfully
    # carried on an event, so they are rejected here rather than silently
    # mangling events later.
    ans = {}
    if not val:
        return ans
    remappable = (
        defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL | defines.GLFW_MOD_SUPER | defines.GLFW_MOD_HYPER | defines.GLFW_MOD_META
    )

    def bad(msg: str) -> None:
        log_error(f'Ignoring invalid remap_modifiers "{val}": {msg}')

    for token in val.split():
        parts = token.split(':', 1)
        if len(parts) != 2:
            bad('must be two modifier names separated by a colon')
            return {}
        # parse_mods() returns None both for an unknown name (already logged) and for
        # "none" (deliberately not logged), so check the names before trusting it
        for part in parts:
            for name in part.split('+'):
                if name.upper() in ('NONE', ''):
                    bad('none is not a modifier')
                    return {}
        src = parse_mods(parts[0].split('+'), val)
        dest = parse_mods(parts[1].split('+'), val)
        if src is None or dest is None:
            return {}  # parse_mods() has already logged the unknown modifier name
        if src & ~remappable or dest & ~remappable:

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Use pairs of remappable modifier names separated by colons: `remap_modifiers alt:ctrl shift:alt`
  2. Only remap shift, alt, ctrl, super, hyper, meta; for caps lock use the OS keyboard settings or kitty's modify_other_keys related options instead
  3. Remove stray tokens that aren't name:name pairs

Example fix

# before
remap_modifiers caps_lock:ctrl
# after
remap_modifiers alt:ctrl
Defensive patterns

Strategy: validation

Validate before calling

val = 'alt:ctrl'
tokens = val.split()
ok = all(len(t.split(':',1)) == 2 for t in tokens) and all(
    p in ('shift','alt','ctrl','super','hyper','meta') for t in tokens for p in t.split(':',1))

Prevention

When it happens

Trigger: Setting `remap_modifiers ctrl:alt extra` (extra token lacks a colon), or `remap_modifiers caps:ctrl` where the modifier isn't in the remappable mask (shift/alt/ctrl/super/hyper/meta).

Common situations: Trying to remap caps lock or other keys not in kitty's remappable set; malformed colon syntax; assuming arbitrary key names are remappable.

Related errors


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