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

  1. Remove the duplicate modifier: \C-\C-a should be just \C-a.
  2. 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.
  3. 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

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


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/ce18a16d47ad0e5f. Report an issue: GitHub.