python/cpython · error · KeySpecError
unterminated \< starting at char %d of %s
Error message
unterminated \< starting at char %d of %s
What it means
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.
Source
Thrown at Lib/_pyrepl/keymap.py:163
)
if meta:
raise KeySpecError(
"doubled \\M- (char %d of %s)" % (s + 1, repr(key))
)
meta = 1
s += 3
elif c.isdigit():
n = key[s + 1 : s + 4]
ret = chr(int(n, 8))
s += 4
elif c == "x":
n = key[s + 2 : s + 4]
ret = chr(int(n, 16))
s += 4
elif c == "<":
t = key.find(">", s)
if t == -1:
raise KeySpecError(
"unterminated \\< starting at char %d of %s"
% (s + 1, repr(key))
)
ret = key[s + 2 : t].lower()
if ret not in _keynames:
raise KeySpecError(
"unrecognised keyname `%s' at char %d of %s"
% (ret, s + 2, repr(key))
)
ret = _keynames[ret]
s = t + 1
else:
raise KeySpecError(
"unknown backslash escape %s at char %d of %s"
% (repr(c), s + 2, repr(key))
)
else:
ret = key[s]View on GitHub (pinned to bc6749cc3b)
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.
Example fix
# before
keymap = {r'\<up': 'previous-history'}
# after
keymap = {r'\<up>': 'previous-history'} Defensive patterns
Strategy: validation
Validate before calling
def closed_keyname(spec):
i = spec.find('\\<')
return i == -1 or spec.find('>', i) != -1 Prevention
- Always close named keys: \<up>.
- Beware shell/f-string truncation of specs containing angle brackets.
- Parse specs at config load to catch truncation early.
When it happens
Trigger: Key specs containing \< with no closing '>', e.g. "\\<up", "\\<home" or a truncated spec string. key.find('>', s) returning -1 is the exact trigger.
Common situations: 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.
Related errors
- \C must be followed by `-' (char %d of %s)
- doubled \C- (char %d of %s)
- \M must be followed by `-' (char %d of %s)
- doubled \M- (char %d of %s)
- unrecognised keyname `%s' at char %d of %s
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/32ae931d2e32aff6.
Report an issue: GitHub.