kovidgoyal/kitty · error · ValueError

Mark group number and text/regex are not specified in pairs:

Error message

Mark group number and text/regex are not specified in pairs: {}

What it means

parse_marker_spec parses set_marker/toggle_marker specs of type text/itext/regex/iregex. It requires an even, non-zero number of parts so each mark group number pairs with a text/regex spec; otherwise it raises ValueError showing the joined parts.

Source

Thrown at kitty/options/utils.py:430

        raise ValueError(f'{strategy} is not a valid disable ligatures strategy')
    return func, [where, strategy]


@func_with_args('layout_action')
def layout_action(func: str, rest: str) -> FuncArgsType:
    parts = rest.split(maxsplit=1)
    if not parts:
        raise ValueError('layout_action must have at least one argument')
    return func, [parts[0], tuple(parts[1:])]


def parse_marker_spec(ftype: str, parts: Sequence[str]) -> tuple[str, str | tuple[tuple[int, str], ...], int]:
    flags = re.UNICODE
    if ftype in ('text', 'itext', 'regex', 'iregex'):
        if ftype.startswith('i'):
            flags |= re.IGNORECASE
        if not parts or len(parts) % 2 != 0:
            raise ValueError('Mark group number and text/regex are not specified in pairs: {}'.format(' '.join(parts)))
        ans = []
        for i in range(0, len(parts), 2):
            try:
                color = max(1, min(int(parts[i]), 3))
            except Exception:
                raise ValueError(f'Mark group in marker specification is not an integer: {parts[i]}')
            sspec = parts[i + 1]
            if 'regex' not in ftype:
                sspec = re.escape(sspec)
            ans.append((color, sspec))
        ftype = 'regex'
        spec: str | tuple[tuple[int, str], ...] = tuple(ans)
    elif ftype == 'function':
        spec = ' '.join(parts)
    else:
        raise ValueError(f'Unknown marker type: {ftype}')
    return ftype, spec, flags

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Ensure arguments come in pairs: <group> <pattern> [<group> <pattern>...]
  2. Group numbers are clamped to 1..3 but must be present and integer-parseable
  3. Recount tokens in the set_marker/toggle_marker invocation

Example fix

# before
kitty @action set_marker text ERROR
# after
kitty @action set_marker text 1 ERROR
Defensive patterns

Strategy: validation

Validate before calling

if not parts or len(parts) % 2 != 0:
    raise SystemExit('marker spec must be non-empty even-length pairs')

Type guard

def is_paired_marker_spec(parts: list[str]) -> bool:
    return bool(parts) and len(parts) % 2 == 0

Prevention

When it happens

Trigger: Calling set_marker with `text foo` (no group number) or `regex 1 foo 2` (odd count) via remote control or a map action like `map f1 set_marker text 1 error 2`.

Common situations: Forgetting the group number (1-3) before each pattern; adding an extra trailing argument; scripts building marker specs dynamically producing odd token counts.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/dfe22305e0192e81. Report an issue: GitHub.