{"record":{"id":"9d620495306b1653","repo":"python/cpython","slug":"key-definitions-for-s-clash","errorCode":null,"errorMessage":"key definitions for %s clash","messagePattern":"key definitions for (.+?) clash","errorType":"validation","errorClass":"KeySpecError","httpStatus":null,"severity":"error","filePath":"Lib/_pyrepl/keymap.py","lineNumber":208,"sourceCode":"\n    result = [ret], s\n    if meta:\n        result[0].insert(0, \"\\033\")\n    return result\n\n\ndef compile_keymap(keymap, empty=b\"\"):\n    r = {}\n    for key, value in keymap.items():\n        if isinstance(key, bytes):\n            first = key[:1]\n        else:\n            first = key[0]\n        r.setdefault(first, {})[key[1:]] = value\n    for key, value in r.items():\n        if empty in value:\n            if len(value) != 1:\n                raise KeySpecError(\"key definitions for %s clash\" % (value.values(),))\n            else:\n                r[key] = value[empty]\n        else:\n            r[key] = compile_keymap(value, empty)\n    return r\n","sourceCodeStart":190,"sourceCodeEnd":214,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyrepl/keymap.py#L190-L214","documentation":"KeySpecError from compile_keymap in Lib/_pyrepl/keymap.py. Keymaps are compiled into a trie keyed by first character with the remainder as a sub-key; the empty remainder marks a complete binding. If one key is a strict prefix of another in the same map (e.g. \\C-x and \\C-x\\C-c), the empty-remainder entry collides with real sub-keys and the definitions are said to clash.","triggerScenarios":"compile_keymap on a dict where both a shorter sequence and longer sequences sharing it as a prefix are bound, e.g. {'x': f1, 'xy': f2} or {'\\\\C-x': 'cmd1', '\\\\C-x\\\\C-c': 'cmd2'}; the check 'empty in value and len(value) != 1' is exactly this conflict.","commonSituations":"Merging keymaps from multiple plugins that bind a prefix and an extension of it; defining both a single-key command and a chord starting with the same key; refactoring keymaps and leaving stale shorter bindings in place.","solutions":["Remove or rename one of the conflicting bindings — a key can be either an action or a prefix for further keys, not both.","If you need a chord, do not bind the bare prefix key to an action; leave it unbound so the trie can descend.","Pre-compile keymaps in tests via _pyrepl.keymap.compile_keymap so clashes fail in CI rather than at REPL startup."],"exampleFix":"# before\nkeymap = {\n    r'\\C-x': 'exit',\n    r'\\C-x\\C-c': 'exit',\n}\n\n# after\nkeymap = {r'\\C-x\\C-c': 'exit'}","handlingStrategy":"validation","validationCode":"def no_prefix_clash(keymap):\n    keys = [k for k in keymap if isinstance(k, str)]\n    for a in keys:\n        for b in keys:\n            if a != b and b.startswith(a):\n                return False\n    return True","typeGuard":null,"tryCatchPattern":"from _pyrepl.keymap import KeySpecError, compile_keymap\ntry:\n    compile_keymap(my_keymap)\nexcept KeySpecError as e:\n    print('clash:', e)","preventionTips":["Never bind both a key and a longer chord sharing that key as prefix.","Compile keymaps in unit tests to catch clashes before shipping.","When merging keymaps, run a prefix-overlap check first."],"tags":["python","pyrepl","keymap","prefix-conflict","trie"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}