666ghj/MiroFish · error · ValueError
Zep search query must be a string
Error message
Zep search query must be a string
What it means
ValueError raised by normalize_zep_search_query when the query argument is not a str instance (None, int, list, dict, bytes). It is a type gate in front of Zep Cloud's search endpoint, which expects a non-empty string capped at MAX_ZEP_SEARCH_QUERY_CHARS (400).
Source
Thrown at backend/app/utils/zep.py:37
T = TypeVar("T")
ZEP_CLOUD_BASE_URL = "https://api.getzep.com/api/v2"
# 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)
View on GitHub (pinned to b5b53acc57)
Solutions
- Coerce or validate the query to a non-empty string before calling the search helper: str(query).strip() if it is scalar, else reject
- At the API/tool boundary, validate request parameters against a schema (e.g. pydantic) so None never reaches here
- For LLM tool calls, re-ask the model or substitute a clear error message when the argument is the wrong type
Example fix
# before
results = zep_search(query=params.get("query")) # None -> ValueError
# after
raw = params.get("query")
if not isinstance(raw, str) or not raw.strip():
raise ValueError("'query' must be a non-empty string")
results = zep_search(query=raw) Defensive patterns
Strategy: type-guard
Validate before calling
if not isinstance(query, str) or not query.strip():
raise ValueError("'query' must be a non-empty string") Type guard
def is_valid_zep_query(q: Any) -> bool:
return isinstance(q, str) and bool(q.strip()) Try / catch
try:
results = zep_search(query=q)
except ValueError as e:
if "must be a string" in str(e):
q = str(q).strip() if q else None
if not q:
raise
results = zep_search(query=q) Prevention
- Validate tool-call/API parameters with a schema (pydantic) at the boundary
- For LLM tool calls, define the query parameter as type string with minLength 1
- Coerce scalars to str early; reject None and containers outright
When it happens
Trigger: Passing a non-string into a Zep search helper: None from an unset variable, a dict/list from upstream JSON, or a number coerced nowhere. The check runs before any network call, so it fails locally and immediately.
Common situations: LLM-generated tool-call arguments (a model emitting null or a nested object for the query parameter), unvalidated API request bodies, or default parameter values like None not replaced before the call.
Related errors
- Zep search query must not be empty
- 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/13e4e9b5f6f227e2.
Report an issue: GitHub.