python/cpython · error · KeySpecError
doubled \C- (char %d of %s)
Error message
doubled \C- (char %d of %s)
What it means
KeySpecError from _parse_single_key_sequence in Lib/_pyrepl/keymap.py. The Ctrl modifier may only be applied once per key spec; a second \C- occurrence in the same spec (while the parser is still accumulating the key) raises this 'doubled \C-' error with the position of the duplicate.
Source
Thrown at Lib/_pyrepl/keymap.py:135
def _parse_single_key_sequence(key: str, s: int) -> tuple[list[str], int]:
ctrl = 0
meta = 0
ret = ""
while not ret and s < len(key):
if key[s] == "\\":
c = key[s + 1].lower()
if c in _escapes:
ret = _escapes[c]
s += 2
elif c == "c":
if key[s + 2] != "-":
raise KeySpecError(
"\\C must be followed by `-' (char %d of %s)"
% (s + 2, repr(key))
)
if ctrl:
raise KeySpecError(
"doubled \\C- (char %d of %s)" % (s + 1, repr(key))
)
ctrl = 1
s += 3
elif c == "m":
if key[s + 2] != "-":
raise KeySpecError(
"\\M must be followed by `-' (char %d of %s)"
% (s + 2, repr(key))
)
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]View on GitHub (pinned to bc6749cc3b)
Solutions
- Remove the duplicate modifier: \C-\C-a should be just \C-a.
- If you intended a two-key chord, keep them as two separate specs/events — the parser returns one key per call, chords are built at the keymap level.
- Lint custom keymaps through _parse_single_key_sequence at startup to catch doubled modifiers early.
Example fix
# before
keymap = {r'\C-\C-a': 'beginning-of-line'}
# after
keymap = {r'\C-a': 'beginning-of-line'} Defensive patterns
Strategy: validation
Validate before calling
import re
def no_doubled_ctrl(spec):
return len(re.findall(r'\\[cC]-', spec)) <= 1 Prevention
- One \C- per key spec; chords are separate specs.
- Parse specs with _parse_single_key_sequence at config load.
- When merging keymaps, check for duplicated modifier tokens.
When it happens
Trigger: A key spec containing two \C- tokens before a base key is produced, e.g. "\\C-\\C-a" or "\\C-x\\C-c" is fine only because parsing restarts per sequence — the error fires when the second \C- appears while ret is still empty, e.g. "\\C-\\C-a".
Common situations: Mistakenly doubling modifiers when translating readline bindings (readline tolerates ^ prefix stacking in some macros); hand-edited keymap tables; macros recorded with doubled control prefixes.
Related errors
- \C must be followed by `-' (char %d of %s)
- \M must be followed by `-' (char %d of %s)
- doubled \M- (char %d of %s)
- unterminated \< starting at 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/ce18a16d47ad0e5f.
Report an issue: GitHub.