{"record":{"id":"32ae931d2e32aff6","repo":"python/cpython","slug":"unterminated-starting-at-char-d-of-s","errorCode":null,"errorMessage":"unterminated \\< starting at char %d of %s","messagePattern":"unterminated \\\\< starting at char (.+?) of (.+?)","errorType":"validation","errorClass":"KeySpecError","httpStatus":null,"severity":"error","filePath":"Lib/_pyrepl/keymap.py","lineNumber":163,"sourceCode":"                    )\n                if meta:\n                    raise KeySpecError(\n                        \"doubled \\\\M- (char %d of %s)\" % (s + 1, repr(key))\n                    )\n                meta = 1\n                s += 3\n            elif c.isdigit():\n                n = key[s + 1 : s + 4]\n                ret = chr(int(n, 8))\n                s += 4\n            elif c == \"x\":\n                n = key[s + 2 : s + 4]\n                ret = chr(int(n, 16))\n                s += 4\n            elif c == \"<\":\n                t = key.find(\">\", s)\n                if t == -1:\n                    raise KeySpecError(\n                        \"unterminated \\\\< starting at char %d of %s\"\n                        % (s + 1, repr(key))\n                    )\n                ret = key[s + 2 : t].lower()\n                if ret not in _keynames:\n                    raise KeySpecError(\n                        \"unrecognised keyname `%s' at char %d of %s\"\n                        % (ret, s + 2, repr(key))\n                    )\n                ret = _keynames[ret]\n                s = t + 1\n            else:\n                raise KeySpecError(\n                    \"unknown backslash escape %s at char %d of %s\"\n                    % (repr(c), s + 2, repr(key))\n                )\n        else:\n            ret = key[s]","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyrepl/keymap.py#L145-L181","documentation":"KeySpecError from _parse_single_key_sequence in Lib/_pyrepl/keymap.py. The \\< escape opens a named-key reference that must be closed with '>' (e.g. \\<up>, \\<f1>). If no '>' is found anywhere after the \\<, the spec is unterminated and this error is raised with the 1-based start position of the \\< token.","triggerScenarios":"Key specs containing \\< with no closing '>', e.g. \"\\\\<up\", \"\\\\<home\" or a truncated spec string. key.find('>', s) returning -1 is the exact trigger.","commonSituations":"Spec strings cut off by shell quoting or line wrapping; typos like \\<up<<; copying specs from docs where the closing '>' was dropped; f-string interpolation accidentally consuming the bracket.","solutions":["Close the named key: \\<up> not \\<up>.","Check the exact keyname against _keynames in _pyrepl/keymap.py (case-insensitive) — wrong names give a different error but truncated ones give this.","Lint every custom spec through _parse_single_key_sequence at startup."],"exampleFix":"# before\nkeymap = {r'\\<up': 'previous-history'}\n\n# after\nkeymap = {r'\\<up>': 'previous-history'}","handlingStrategy":"validation","validationCode":"def closed_keyname(spec):\n    i = spec.find('\\\\<')\n    return i == -1 or spec.find('>', i) != -1","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always close named keys: \\<up>.","Beware shell/f-string truncation of specs containing angle brackets.","Parse specs at config load to catch truncation early."],"tags":["python","pyrepl","keymap","parsing","repl"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}