kovidgoyal/kitty · error · ParseException
Failed to parse {query!r}, too much recursion required
Error message
Failed to parse {query!r}, too much recursion required What it means
Raised when the recursive-descent parser blows Python's recursion limit while parsing a search query, i.e. the expression is too deeply nested or too long. The underlying RecursionError from parse() is caught in build_tree and re-raised as a ParseException with this message.
Source
Thrown at kitty/search_query_parser.py:290
tt = self.token(advance=True)
assert tt is not None
return TokenNode(loc, tt)
return TokenNode(loc.lower(), ':'.join(words))
if self.allow_no_location:
return TokenNode('all', ':'.join(words))
raise NoLocation(tt)
@lru_cache(maxsize=64)
def build_tree(query: str, locations: str | tuple[str, ...], allow_no_location: bool = False) -> SearchTreeNode:
if isinstance(locations, str):
locations = tuple(locations.split())
p = Parser(allow_no_location)
try:
return p.parse(query, locations)
except RuntimeError as e:
raise ParseException(f'Failed to parse {query!r}, too much recursion required') from e
def search(
query: str,
locations: str | tuple[str, ...],
universal_set: set[T],
get_matches: GetMatches[T],
allow_no_location: bool = False,
) -> set[T]:
return build_tree(query, locations, allow_no_location).search(universal_set, get_matches)
View on GitHub (pinned to 6d5d0c4406)
Solutions
- Flatten the query: use a flat chain of 'and'/'or' instead of nested parentheses
- Limit or batch the terms you feed into one query
- Catch ParseException at the call site and reject/split the query
- If legitimately deep nesting is required, raise sys.setrecursionlimit cautiously — better to restructure the query
Example fix
# before
q = '(' * 5000 + 'foo' + ')' * 5000
build_tree(q, locations)
# after
q = ' and '.join(terms) # flat conjunction
build_tree(q, locations) Defensive patterns
Strategy: try-catch
Validate before calling
def depth_ok(q: str, limit: int = 200) -> bool:
depth = mx = 0
for ch in q:
if ch == '(':
depth += 1
mx = max(mx, depth)
elif ch == ')':
depth -= 1
return mx <= limit and len(q) < 100_000 Try / catch
from kitty.search_query_parser import ParseException
try:
node = build_tree(query, locations)
except ParseException as e:
if 'too much recursion' in str(e):
query = flatten_query(query) # or split into batches
else:
raise Prevention
- Prefer flat and/or chains over nesting
- Cap query length and nesting depth at generation time
- Catch ParseException and degrade gracefully
When it happens
Trigger: Queries with very deep parenthesis nesting (hundreds of levels), extremely long chains of 'and'/'or' terms, or machine-generated queries of pathological length passed to kitty.search.query_parser.search()/build_tree().
Common situations: Programmatically generated queries from large term lists; adversarial/fuzzed input fed to the parser; nesting from auto-wrapping logic that adds parens per iteration.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Extra characters at end of search
- missing )
- Invalid syntax. Expected a lookup name or a word
- Too many nested include directives while processing config f
- This must be run as kitten ask
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/399105e7f9bfbced.
Report an issue: GitHub.