kovidgoyal/kitty · error · ValueError

The menu entry {val} must have an action

Error message

The menu entry {val} must have an action

What it means

Raised by kitty's menu action parser when a menu entry value (e.g. a 'global' menu specification) has no action part after the location. The parser splits the value after the location token and searches for a space to separate the menu path from the action; if no space/action is found the value is considered malformed.

Source

Thrown at kitty/options/utils.py:1161

        yield val, val


def menu_map(val: str, current_val: Container[str]) -> Iterable[tuple[tuple[str, ...], str]]:
    parts = val.split(maxsplit=1)
    if len(parts) != 2:
        raise ValueError(f'Ignoring invalid menu action: {val}')
    if parts[0] != 'global':
        raise ValueError(f'Unknown menu type: {parts[0]}. Known types: global')
    start = 0
    if parts[1].startswith('"'):
        start = 1
        idx = parts[1].find('"', 1)
        if idx == -1:
            raise ValueError(f'The menu entry name in {val} must end with a double quote')
    else:
        idx = parts[1].find(' ')
        if idx == -1:
            raise ValueError(f'The menu entry {val} must have an action')
    location = ('global',) + tuple(parts[1][start:idx].split('::'))
    yield location, parts[1][idx + 1 :].lstrip()


allowed_shell_integration_values = frozenset({'enabled', 'disabled', 'no-rc', 'no-cursor', 'no-title', 'no-prompt-mark', 'no-complete', 'no-cwd', 'no-sudo'})


def shell_integration(x: str) -> frozenset[str]:
    q = frozenset(x.lower().split())
    if not q.issubset(allowed_shell_integration_values):
        log_error(f'Invalid shell integration options: {q - allowed_shell_integration_values}, ignoring')
        return q & allowed_shell_integration_values or frozenset({'invalid'})
    return q


def confirm_close(x: str) -> tuple[int, bool]:
    parts = x.split(maxsplit=1)
    num = int(parts[0])

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Add the action after the menu path, separated by whitespace
  2. Quote the menu name correctly if it contains spaces and still append an action
  3. Check kitty docs for the menu entry syntax for the option you are configuring

Example fix

# before
menu_entry 'global "File::Open"'
# after
menu_entry 'global "File::Open" :: open_file'
Defensive patterns

Strategy: validation

Validate before calling

import re
def valid_menu_entry(v: str) -> bool:
    return bool(re.search(r'\s', v.strip().split(maxsplit=1)[-1]))

Prevention

When it happens

Trigger: Setting a menu-related option like `map ... global::Menu/Item` variant entries where the string after parsing contains a quoted location but no trailing action text, e.g. 'global "File::Open"' with nothing after it, or an unquoted location with no space-delimited action.

Common situations: Typos in kitten/config menu definitions, forgetting the action argument at the end of a menu entry, or copying a partial example from docs.

Related errors


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