{"record":{"id":"ba9dacc1e249cfb2","repo":"kovidgoyal/kitty","slug":"ignoring-invalid-remap-modifiers-val-msg","errorCode":null,"errorMessage":"Ignoring invalid remap_modifiers \"{val}\": {msg}","messagePattern":"Ignoring invalid remap_modifiers \"(.+?)\": (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kitty/options/utils.py","lineNumber":541,"sourceCode":"def to_modifiers(val: str) -> int:\n    return parse_mods(val.split('+'), val) or 0\n\n\ndef remap_modifiers(val: str) -> dict[int, int]:\n    # Only the real modifiers may be remapped. parse_mods() also accepts\n    # kitty_mod (a parse-time placeholder resolved out of keymaps before any key\n    # event exists) and the lock modifiers, neither of which can be meaningfully\n    # carried on an event, so they are rejected here rather than silently\n    # mangling events later.\n    ans = {}\n    if not val:\n        return ans\n    remappable = (\n        defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL | defines.GLFW_MOD_SUPER | defines.GLFW_MOD_HYPER | defines.GLFW_MOD_META\n    )\n\n    def bad(msg: str) -> None:\n        log_error(f'Ignoring invalid remap_modifiers \"{val}\": {msg}')\n\n    for token in val.split():\n        parts = token.split(':', 1)\n        if len(parts) != 2:\n            bad('must be two modifier names separated by a colon')\n            return {}\n        # parse_mods() returns None both for an unknown name (already logged) and for\n        # \"none\" (deliberately not logged), so check the names before trusting it\n        for part in parts:\n            for name in part.split('+'):\n                if name.upper() in ('NONE', ''):\n                    bad('none is not a modifier')\n                    return {}\n        src = parse_mods(parts[0].split('+'), val)\n        dest = parse_mods(parts[1].split('+'), val)\n        if src is None or dest is None:\n            return {}  # parse_mods() has already logged the unknown modifier name\n        if src & ~remappable or dest & ~remappable:","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kitty/options/utils.py#L523-L559","documentation":"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.","triggerScenarios":"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).","commonSituations":"Trying to remap caps lock or other keys not in kitty's remappable set; malformed colon syntax; assuming arbitrary key names are remappable.","solutions":["Use pairs of remappable modifier names separated by colons: `remap_modifiers alt:ctrl shift:alt`","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","Remove stray tokens that aren't name:name pairs"],"exampleFix":"# before\nremap_modifiers caps_lock:ctrl\n# after\nremap_modifiers alt:ctrl","handlingStrategy":"validation","validationCode":"val = 'alt:ctrl'\ntokens = val.split()\nok = all(len(t.split(':',1)) == 2 for t in tokens) and all(\n    p in ('shift','alt','ctrl','super','hyper','meta') for t in tokens for p in t.split(':',1))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["remap_modifiers accepts only colon-separated pairs of shift/alt/ctrl/super/hyper/meta","One malformed token disables the entire option","Use OS-level remapping for keys like caps lock"],"tags":["kitty","config","keyboard","modifiers","parser"],"backgroundTag":"invalid-keybinding-modifier","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}