BerriAI/litellm · error · ValueError

REDIS_CLUSTER_NODES environment variable is not valid JSON…

Error message

REDIS_CLUSTER_NODES environment variable is not valid JSON. Please ensure it's properly formatted.

What it means

init_redis_cluster() reads REDIS_CLUSTER_NODES (via get_secret) and, when set, must json.loads it into startup_nodes. Malformed JSON raises ValueError('REDIS_CLUSTER_NODES environment variable is not valid JSON...'). The variable must be a JSON array like [{"host": "10.0.0.1", "port": 6379}, ...] — a plain comma-separated host list is not accepted.

Solutions

  1. Set the value as strict JSON: export REDIS_CLUSTER_NODES='[{"host":"10.0.0.1","port":6379},{"host":"10.0.0.2","port":6379}]'
  2. Validate locally first: python -c "import json,os; json.loads(os.environ['REDIS_CLUSTER_NODES'])"
  3. Prefer passing startup_nodes explicitly in cache config instead of the env var
  4. Check k8s secret quoting: use stringData with the raw JSON and no extra escapes

Example fix

# before
export REDIS_CLUSTER_NODES="redis-0:6379,redis-1:6379"  # ValueError: not valid JSON

# after
export REDIS_CLUSTER_NODES='[{"host":"redis-0","port":6379},{"host":"redis-1","port":6379}]'
Defensive patterns

Strategy: validation

Validate before calling

import json, os

raw = os.environ.get("REDIS_CLUSTER_NODES")
if raw is not None:
    try:
        nodes = json.loads(raw)
        assert isinstance(nodes, list) and all("host" in n for n in nodes)
    except (json.JSONDecodeError, AssertionError) as e:
        raise ValueError(
            f"REDIS_CLUSTER_NODES must be JSON like '[{{\"host\":..,\"port\":..}}]': {e}"
        ) from None

Try / catch

import json

try:
    nodes = json.loads(raw_cluster_nodes)
except json.JSONDecodeError as e:
    raise ValueError(f"fix REDIS_CLUSTER_NODES JSON: {e}") from e

Prevention

When it happens

Trigger: Setting REDIS_CLUSTER_NODES="redis-1:6379,redis-2:6379" (comma format) instead of JSON; trailing semicolons or smart quotes from copy-paste; single quotes instead of double quotes (YAML-style leaking into env); truncated value from k8s secret encoding.

Common situations: Porting a docker-compose/k8s example that used a different format; templating (Helm/Terraform) producing invalid JSON; secrets stored with surrounding quotes that break parsing.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/104a2cb06510a693. Report an issue: GitHub.

Appendix: source

Thrown at litellm/_redis.py:495

        and redis_kwargs["startup_nodes"] is not None
        or "sentinel_nodes" in redis_kwargs
        and redis_kwargs["sentinel_nodes"] is not None
    ):
        pass
    elif "host" not in redis_kwargs or redis_kwargs["host"] is None:
        raise ValueError("Either 'host' or 'url' must be specified for redis.")

    # litellm.print_verbose(f"redis_kwargs: {redis_kwargs}")
    return redis_kwargs


def init_redis_cluster(redis_kwargs) -> redis.RedisCluster:
    _redis_cluster_nodes_in_env: Final[str | None] = get_secret("REDIS_CLUSTER_NODES")
    if _redis_cluster_nodes_in_env is not None:
        try:
            redis_kwargs["startup_nodes"] = json.loads(_redis_cluster_nodes_in_env)
        except json.JSONDecodeError:
            raise ValueError(
                "REDIS_CLUSTER_NODES environment variable is not valid JSON. Please ensure it's properly formatted."
            )

    verbose_logger.debug("init_redis_cluster: startup nodes are being initialized.")
    from redis.cluster import ClusterNode

    args: Final = _get_redis_cluster_kwargs()
    cluster_kwargs: Final = {}
    for arg in redis_kwargs:
        if arg in args:
            cluster_kwargs[arg] = redis_kwargs[arg]

    new_startup_nodes: Final[list[ClusterNode]] = []

    for item in redis_kwargs["startup_nodes"]:
        new_startup_nodes.append(ClusterNode(**item))

    cluster_kwargs.pop("startup_nodes", None)

View on GitHub (pinned to 77b7c6c40c)