666ghj/MiroFish · error · ValueError

Zep search limit must be at least 1

Error message

Zep search limit must be at least 1

What it means

Raised by normalize_zep_search_limit in backend/app/utils/zep.py when limit converts to an int but is below 1 (0 or negative). Zep Cloud's search API requires limit >= 1, so the guard rejects degenerate values up front. After this check the value is clamped with min(normalized, MAX_ZEP_SEARCH_RESULTS) where MAX_ZEP_SEARCH_RESULTS=50, so anything >= 1 is safe.

Source

Thrown at backend/app/utils/zep.py:52

    """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.
    # Reject it so this Cloud-only integration cannot silently target a
    # self-hosted or compatibility endpoint.

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Coerce non-positive limits to a sensible default before calling: max(limit, 1) or replace 0/negatives with e.g. 10.
  2. Validate user-supplied limit as an integer in 1..50 at the API boundary and reject out-of-range input there.
  3. Never use 0 as an 'unset' sentinel for this API; resolve None yourself to a default.

Example fix

# before
results = search(query=q, limit=requested)  # requested = 0

# after
limit = requested if requested and requested > 0 else 10
results = search(query=q, limit=limit)
Defensive patterns

Strategy: validation

Validate before calling

def safe_limit(raw: Any, default: int = 10) -> int:
    try:
        value = int(raw)
    except (TypeError, ValueError):
        return default
    return value if value >= 1 else default

Type guard

def is_positive_limit(value: Any) -> TypeGuard[int]:
    if isinstance(value, bool):
        return False
    try:
        return int(value) >= 1
    except (TypeError, ValueError):
        return False

Try / catch

try:
    limit = normalize_zep_search_limit(raw)
except ValueError as e:
    if "at least 1" in str(e):
        limit = 1  # minimum meaningful page
    else:
        raise

Prevention

When it happens

Trigger: Passing limit=0 (common 'unset' sentinel mistake), negative values from arithmetic (page-size formulas that underflow), or user input "0"/"-5" parsed to int and forwarded.

Common situations: Caller uses 0 to mean 'no preference' expecting a default; a limit computed from page_size/offset math that can reach 0 or go negative on edge inputs.

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/c83712c37829fdce. Report an issue: GitHub.