{"record":{"id":"dfe22305e0192e81","repo":"kovidgoyal/kitty","slug":"mark-group-number-and-text-regex-are-not-specified","errorCode":null,"errorMessage":"Mark group number and text/regex are not specified in pairs: {}","messagePattern":"Mark group number and text/regex are not specified in pairs: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"kitty/options/utils.py","lineNumber":430,"sourceCode":"        raise ValueError(f'{strategy} is not a valid disable ligatures strategy')\n    return func, [where, strategy]\n\n\n@func_with_args('layout_action')\ndef layout_action(func: str, rest: str) -> FuncArgsType:\n    parts = rest.split(maxsplit=1)\n    if not parts:\n        raise ValueError('layout_action must have at least one argument')\n    return func, [parts[0], tuple(parts[1:])]\n\n\ndef parse_marker_spec(ftype: str, parts: Sequence[str]) -> tuple[str, str | tuple[tuple[int, str], ...], int]:\n    flags = re.UNICODE\n    if ftype in ('text', 'itext', 'regex', 'iregex'):\n        if ftype.startswith('i'):\n            flags |= re.IGNORECASE\n        if not parts or len(parts) % 2 != 0:\n            raise ValueError('Mark group number and text/regex are not specified in pairs: {}'.format(' '.join(parts)))\n        ans = []\n        for i in range(0, len(parts), 2):\n            try:\n                color = max(1, min(int(parts[i]), 3))\n            except Exception:\n                raise ValueError(f'Mark group in marker specification is not an integer: {parts[i]}')\n            sspec = parts[i + 1]\n            if 'regex' not in ftype:\n                sspec = re.escape(sspec)\n            ans.append((color, sspec))\n        ftype = 'regex'\n        spec: str | tuple[tuple[int, str], ...] = tuple(ans)\n    elif ftype == 'function':\n        spec = ' '.join(parts)\n    else:\n        raise ValueError(f'Unknown marker type: {ftype}')\n    return ftype, spec, flags\n","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kitty/options/utils.py#L412-L448","documentation":"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.","triggerScenarios":"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`.","commonSituations":"Forgetting the group number (1-3) before each pattern; adding an extra trailing argument; scripts building marker specs dynamically producing odd token counts.","solutions":["Ensure arguments come in pairs: <group> <pattern> [<group> <pattern>...]","Group numbers are clamped to 1..3 but must be present and integer-parseable","Recount tokens in the set_marker/toggle_marker invocation"],"exampleFix":"# before\nkitty @action set_marker text ERROR\n# after\nkitty @action set_marker text 1 ERROR","handlingStrategy":"validation","validationCode":"if not parts or len(parts) % 2 != 0:\n    raise SystemExit('marker spec must be non-empty even-length pairs')","typeGuard":"def is_paired_marker_spec(parts: list[str]) -> bool:\n    return bool(parts) and len(parts) % 2 == 0","tryCatchPattern":null,"preventionTips":["Always pass <group> <pattern> pairs","Test script-generated marker specs before running"],"tags":["kitty","markers","set-marker","action-args","pairing"],"backgroundTag":"argument-pair-validation","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}