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
- Add the action after the menu path, separated by whitespace
- Quote the menu name correctly if it contains spaces and still append an action
- 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
- Always end menu entries with an action token
- Validate config with `kitty --debug-config` before reloading
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
- Failed to parse %s = %#v with error: %w
- Failed to parse map = %#v with error: %w
- This should be run as kitten broadcast
- No option named: {k}
- No custom kitten named {kitten} found
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/ad882821a9e4a0dc.
Report an issue: GitHub.