python/cpython · error · KeySpecError

\C must be followed by `-' (char %d of %s)

Error message

\C must be followed by `-' (char %d of %s)

What it means

KeySpecError from _parse_single_key_sequence in _pyrepl/keymap.py. When parsing a key specification string, the sequence \c must be followed by a literal '-' (i.e. \C-), because the parser only accepts Ctrl modifiers in the canonical \C-x form. Any other character after \c triggers this error with the 1-based offset into the spec.

Source

Thrown at Lib/_pyrepl/keymap.py:130

    while s < len(keys):
        k, s = _parse_single_key_sequence(keys, s)
        r.extend(k)
    return r


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))

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Write the modifier with its dash: \C-x, not \cx or \C-x without the hyphen.
  2. For plain characters use the raw character or a named key like \<Ctrl-x>-style spelled forms the parser supports; check _keynames for recognized names.
  3. Validate custom key specs at startup by calling _pyrepl.keymap._parse_single_key_sequence on each so config typos fail loudly at load, not at keypress.

Example fix

# before
keymap = {r'\cx': 'cut'}

# after
keymap = {r'\C-x': 'cut'}
Defensive patterns

Strategy: validation

Validate before calling

from _pyrepl.keymap import _parse_single_key_sequence

def valid_keyspec(spec):
    try:
        _parse_single_key_sequence(spec, 0)
        return True
    except Exception:
        return False

assert valid_keyspec(r'\C-x'), 'spec must be \\C-<key> with dash'

Try / catch

from _pyrepl.keymap import KeySpecError
try:
    _parse_single_key_sequence(r'\cx', 0)
except KeySpecError as e:
    print('bad key spec:', e)

Prevention

When it happens

Trigger: Calling _parse_single_key_sequence (or compiling a keymap whose keys go through it) with a spec containing \c not followed by '-', e.g. "\\cX", "\\Cx", or "\\c[3~". Case is lowered, so both \C and \c are affected.

Common situations: Porting readline-style bindings like \C-x\C-c into pyrepl config and dropping the dash; copy-pasting key specs from readline docs (which accept ^X notation) into pyrepl keymaps; user keymaps in sitecustomize or REPL plugins.

Related errors


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