666ghj/MiroFish · error · ValueError

ZEP_API_URL is unsupported; unset it to use Zep Cloud

Error message

ZEP_API_URL is unsupported; unset it to use Zep Cloud

What it means

Raised by get_zep_client in backend/app/utils/zep.py when os.environ.get("ZEP_API_URL") is truthy. The comment in the source explains why: zep-cloud gives ZEP_API_URL precedence even when base_url is explicit (the client is built with base_url=ZEP_CLOUD_BASE_URL), so this Cloud-only integration rejects the variable to prevent silently targeting a self-hosted/compatibility endpoint. Note an empty-string value does not trigger it (falsy check).

Source

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


@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.
    if os.environ.get("ZEP_API_URL"):
        raise ValueError("ZEP_API_URL is unsupported; unset it to use Zep Cloud")

    normalized_key = (api_key or Config.ZEP_API_KEY or "").strip()
    if not normalized_key:
        raise ValueError("ZEP_API_KEY 未配置")

    request_timeout = float(
        timeout if timeout is not None else ZEP_HTTP_REQUEST_TIMEOUT_SECONDS
    )
    if request_timeout <= 0:
        raise ValueError("Zep request timeout must be greater than 0")
    return _cached_zep_client(normalized_key, request_timeout)


def clear_zep_client_cache() -> None:
    """Clear cached clients. Intended for tests and controlled reconfiguration."""

    _cached_zep_client.cache_clear()

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Unset the variable (unset ZEP_API_URL) and remove it from .env, docker-compose environment:, and K8s ConfigMap/Secret refs — any one non-empty occurrence triggers this.
  2. Search the whole environment chain (shell profile, .env files, container specs) if the source is not obvious.
  3. If you actually need self-hosted Zep, this integration does not support it — do not bypass the guard; use a separate code path with the OSS client.

Example fix

# before (.env)
ZEP_API_URL=http://localhost:8000
ZEP_API_KEY=...

# after (.env)
ZEP_API_KEY=...
Defensive patterns

Strategy: validation

Validate before calling

import os

def zep_env_is_clean() -> bool:
    return not os.environ.get("ZEP_API_URL")

Try / catch

try:
    client = get_zep_client()
except ValueError as e:
    if "ZEP_API_URL" in str(e):
        os.environ.pop("ZEP_API_URL", None)
        client = get_zep_client()  # retry with cleaned env
    else:
        raise

Prevention

When it happens

Trigger: ZEP_API_URL set to any non-empty value in the shell, .env, Docker/Kubernetes env, or CI secrets while get_zep_client() is called — e.g. ZEP_API_URL=http://localhost:8000 left over from self-hosted Zep.

Common situations: Migrating from open-source Zep to Zep Cloud and forgetting to remove ZEP_API_URL; a shared .env mixing OSS and Cloud settings; a deployment manifest copied from a self-hosted setup.

Related errors


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