kovidgoyal/kitty · error · ParseException
Invalid syntax. Expected a lookup name or a word
Error message
Invalid syntax. Expected a lookup name or a word
What it means
Raised when the parser reaches a position where a word, quoted word, or '(' was expected but finds something else (an operator like 'and'/'or'/'not' in the wrong place, or a ')' too early). It is the grammar's 'expected a terminal' error in location_expression.
Source
Thrown at kitty/search_query_parser.py:241
if (self.token_type() in (TokenType.WORD, TokenType.QUOTED_WORD) or self.token() == '(') and self.lcase_token() != 'or':
return AndNode(lhs, self.and_expression())
return lhs
def not_expression(self) -> SearchTreeNode:
if self.lcase_token() == 'not':
self.advance()
return NotNode(self.not_expression())
return self.location_expression()
def location_expression(self) -> SearchTreeNode:
if self.token_type() == TokenType.OPCODE and self.token() == '(':
self.advance()
res = self.or_expression()
if self.token_type() != TokenType.OPCODE or self.token(advance=True) != ')':
raise ParseException(_('missing )'))
return res
if self.token_type() not in (TokenType.WORD, TokenType.QUOTED_WORD):
raise ParseException(_('Invalid syntax. Expected a lookup name or a word'))
return self.base_token()
def base_token(self) -> SearchTreeNode:
if self.token_type() is TokenType.QUOTED_WORD:
tt = self.token(advance=True)
assert tt is not None
if self.allow_no_location:
return TokenNode('all', tt)
raise NoLocation(tt)
tt = self.token(advance=True)
assert tt is not None
words = tt.split(':')
# The complexity here comes from having colon-separated search
# values. That forces us to check that the first "word" in a colon-
# separated group is a valid location. If not, then the token must
# be reconstructed. We also have the problem that locations can beView on GitHub (pinned to 6d5d0c4406)
Solutions
- Remove or fix dangling operators: never start a query with 'and'/'or', never put two operators in a row
- When concatenating optional fragments, filter out empty parts first: ' and '.join(x for x in parts if x)
- Avoid empty parentheses '()'
Example fix
# before parts = [term, optional_filter] q = ' and '.join(parts) # optional_filter may be '' # after q = ' and '.join(p for p in [term, optional_filter] if p)
Defensive patterns
Strategy: validation
Validate before calling
import re
def wellformed(q: str) -> bool:
# reject empty groups and dangling operators
if '()' in q or re.search(r'(^|\s)(and|or|not)(\s|$)*$', q) or re.search(r'^(and|or)(\s|$)', q):
return False
return not re.search(r'\b(and|or|not)\s+(and|or|not)\b', q) Try / catch
try:
node = build_tree(query, locations)
except ParseException as e:
show_error(f'Invalid query syntax: {e}') Prevention
- Filter empty fragments before joining with and/or
- Never place two operators adjacently
- Dry-run generated queries through search()
When it happens
Trigger: Queries like 'and foo' (leading operator), 'foo and or bar' (two consecutive operators), 'not and foo', or '()' (empty parentheses).
Common situations: Typos in search queries; concatenating optional filter fragments such that an 'and' is left dangling when one side is empty (e.g. f'{term} and {optional_filter}' with optional_filter='').
Related errors
- missing )
- Extra characters at end of search
- Failed to parse {query!r}, too much recursion required
- Bad config lines: %s with error: %s
- This must be run as kitten ask
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/d129d8e3219e3532.
Report an issue: GitHub.