666ghj/MiroFish · error · ValueError
Zep search limit must be an integer
Error message
Zep search limit must be an integer
What it means
Raised by normalize_zep_search_limit in backend/app/utils/zep.py when int(limit) raises TypeError or ValueError — i.e. limit is None, a non-numeric string ("", "ten"), or a non-numeric type like dict/list. Numeric strings ("10") and floats (10.0) convert fine and do NOT trigger this. It is a ValueError chained from the original exception, raised before any Zep Cloud call.
Source
Thrown at backend/app/utils/zep.py:50
def normalize_zep_search_query(query: Any) -> str:
"""Return a non-empty query within Zep Cloud's endpoint limit."""
if not isinstance(query, str):
raise ValueError("Zep search query must be a string")
normalized = query.strip()
if not normalized:
raise ValueError("Zep search query must not be empty")
return normalized[:MAX_ZEP_SEARCH_QUERY_CHARS]
def normalize_zep_search_limit(limit: Any) -> int:
"""Clamp a search result limit to the current Zep Cloud contract."""
try:
normalized = int(limit)
except (TypeError, ValueError) as exc:
raise ValueError("Zep search limit must be an integer") from exc
if normalized < 1:
raise ValueError("Zep search limit must be at least 1")
return min(normalized, MAX_ZEP_SEARCH_RESULTS)
@lru_cache(maxsize=4)
def _cached_zep_client(api_key: str, timeout: float) -> Zep:
return Zep(
api_key=api_key,
base_url=ZEP_CLOUD_BASE_URL,
timeout=timeout,
)
def get_zep_client(api_key: str | None = None, timeout: float | None = None) -> Zep:
"""Return a process-shared, explicitly configured Zep Cloud client."""
# zep-cloud gives ZEP_API_URL precedence even when base_url is explicit.View on GitHub (pinned to b5b53acc57)
Solutions
- Resolve None/empty to an explicit default at the call site before calling: limit = int(raw) if raw else 10.
- Parse query/config limits with a typed parser (Pydantic field, argparse type=int) at the boundary.
- Pass an int literal when no configurability is needed.
Example fix
# before
results = search(query=q, limit=request.args.get("limit")) # str or None
# after
raw = request.args.get("limit")
limit = int(raw) if raw else 10
results = search(query=q, limit=limit) Defensive patterns
Strategy: validation
Validate before calling
def coerce_limit(raw: Any, default: int = 10) -> int:
if raw is None or raw == "":
return default
return int(raw) # surface bad input early at the boundary Type guard
def is_int_like_limit(value: Any) -> TypeGuard[int]:
if isinstance(value, bool):
return False
try:
int(value)
return True
except (TypeError, ValueError):
return False Try / catch
try:
limit = normalize_zep_search_limit(raw_limit)
except ValueError:
limit = 10 # sane default for untrusted input Prevention
- Parse query-string/config limits with typed parsers (Pydantic, type=int) before they reach the Zep layer.
- Never forward None for limit; resolve None to an explicit default yourself.
- Beware bools: int(True) == 1 passes conversion — reject bool explicitly if it can reach this path.
When it happens
Trigger: Passing limit=None (there is no None-default handling in this function), limit="", or limit={"size": 10}. Forwarding unparsed HTTP query-string parameters (always str or None) directly as limit.
Common situations: A handler doing request.args.get("limit") and forwarding it; a caller passing None expecting a library default to apply; config values arriving as empty strings when the field is unset.
Related errors
- Zep search limit must be at least 1
- Zep batch list cursor did not advance
- graph_id is required
- At least one text chunk is required
- batch_size must be between 1 and 350
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/2d0599f90ba871db.
Report an issue: GitHub.