666ghj/MiroFish · error · ValueError
Zep search query must not be empty
Error message
Zep search query must not be empty
What it means
Raised by normalize_zep_search_query in backend/app/utils/zep.py when the query argument is a str but strips to an empty string (only whitespace). The function is a precondition guard for Zep Cloud's graph search endpoint, which rejects blank queries upstream; failing fast here gives a clearer error before any network call. It is a ValueError raised before any HTTP request.
Source
Thrown at backend/app/utils/zep.py:40
# Keep request behavior aligned with the zep-cloud 3.25.0 SDK default that
# MiroFish used before introducing the shared client. This is an internal
# integration policy, not a deployment setting users need to tune.
ZEP_HTTP_REQUEST_TIMEOUT_SECONDS = 60.0
# Zep ingestion is asynchronous and may take several minutes. Preserve the
# original GraphBuilder deadline while keeping it separate from HTTP timeout.
ZEP_INGESTION_WAIT_TIMEOUT_SECONDS = 600
MAX_ZEP_SEARCH_QUERY_CHARS = 400
MAX_ZEP_SEARCH_RESULTS = 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(View on GitHub (pinned to b5b53acc57)
Solutions
- Check query and query.strip() at the caller before invoking the search; return an empty result or re-prompt instead of calling the API.
- Validate user-supplied search text at the request boundary (422-style error) so blank input never reaches the Zep layer.
- Note the function also truncates to MAX_ZEP_SEARCH_QUERY_CHARS (400); pass pre-trimmed, reasonably sized queries so behavior is predictable.
Example fix
# before
results = zep_search(query=user_input) # user_input = " "
# after
if not user_input or not user_input.strip():
return []
results = zep_search(query=user_input.strip()) Defensive patterns
Strategy: validation
Validate before calling
def is_valid_zep_query(query: Any) -> bool:
return isinstance(query, str) and bool(query.strip()) Type guard
def is_non_empty_query(query: Any) -> TypeGuard[str]:
return isinstance(query, str) and len(query.strip()) > 0 Try / catch
try:
results = zep_search(query=q, limit=10)
except ValueError as e:
if "must not be empty" in str(e):
results = [] # blank query is a no-op
else:
raise Prevention
- Strip and reject blank search text once at the API boundary (422) instead of at the Zep layer.
- In agent loops, skip the search step when the generated query is blank.
- Pre-trim queries to 400 chars so the truncation behavior is predictable.
When it happens
Trigger: Calling a Zep search wrapper that uses normalize_zep_search_query with query="", query=" ", or a string of only newlines/tabs. Any caller forwarding user text or LLM-generated text without a blank check hits this.
Common situations: A chat/RAG endpoint where the user submits an empty message; an agent loop passing an LLM-generated search string that happens to be whitespace; a config default of "" for a search term.
Related errors
- Zep search query must be a string
- graph_id is required
- At least one text chunk is required
- batch_size must be between 1 and 350
- A Zep batch cannot contain more than 50,000 items
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/71e8e85894afeb52.
Report an issue: GitHub.