{"record":{"id":"104a2cb06510a693","repo":"BerriAI/litellm","slug":"redis-cluster-nodes-environment-variable-is-not-va","errorCode":null,"errorMessage":"REDIS_CLUSTER_NODES environment variable is not valid JSON. Please ensure it's properly formatted.","messagePattern":"REDIS_CLUSTER_NODES environment variable is not valid JSON\\. Please ensure it's properly formatted\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/_redis.py","lineNumber":495,"sourceCode":"        and redis_kwargs[\"startup_nodes\"] is not None\n        or \"sentinel_nodes\" in redis_kwargs\n        and redis_kwargs[\"sentinel_nodes\"] is not None\n    ):\n        pass\n    elif \"host\" not in redis_kwargs or redis_kwargs[\"host\"] is None:\n        raise ValueError(\"Either 'host' or 'url' must be specified for redis.\")\n\n    # litellm.print_verbose(f\"redis_kwargs: {redis_kwargs}\")\n    return redis_kwargs\n\n\ndef init_redis_cluster(redis_kwargs) -> redis.RedisCluster:\n    _redis_cluster_nodes_in_env: Final[str | None] = get_secret(\"REDIS_CLUSTER_NODES\")\n    if _redis_cluster_nodes_in_env is not None:\n        try:\n            redis_kwargs[\"startup_nodes\"] = json.loads(_redis_cluster_nodes_in_env)\n        except json.JSONDecodeError:\n            raise ValueError(\n                \"REDIS_CLUSTER_NODES environment variable is not valid JSON. Please ensure it's properly formatted.\"\n            )\n\n    verbose_logger.debug(\"init_redis_cluster: startup nodes are being initialized.\")\n    from redis.cluster import ClusterNode\n\n    args: Final = _get_redis_cluster_kwargs()\n    cluster_kwargs: Final = {}\n    for arg in redis_kwargs:\n        if arg in args:\n            cluster_kwargs[arg] = redis_kwargs[arg]\n\n    new_startup_nodes: Final[list[ClusterNode]] = []\n\n    for item in redis_kwargs[\"startup_nodes\"]:\n        new_startup_nodes.append(ClusterNode(**item))\n\n    cluster_kwargs.pop(\"startup_nodes\", None)","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/_redis.py#L477-L513","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set the value as strict JSON: export REDIS_CLUSTER_NODES='[{\"host\":\"10.0.0.1\",\"port\":6379},{\"host\":\"10.0.0.2\",\"port\":6379}]'","Validate locally first: python -c \"import json,os; json.loads(os.environ['REDIS_CLUSTER_NODES'])\"","Prefer passing startup_nodes explicitly in cache config instead of the env var","Check k8s secret quoting: use stringData with the raw JSON and no extra escapes"],"exampleFix":"# before\nexport REDIS_CLUSTER_NODES=\"redis-0:6379,redis-1:6379\"  # ValueError: not valid JSON\n\n# after\nexport REDIS_CLUSTER_NODES='[{\"host\":\"redis-0\",\"port\":6379},{\"host\":\"redis-1\",\"port\":6379}]'","handlingStrategy":"validation","validationCode":"import json, os\n\nraw = os.environ.get(\"REDIS_CLUSTER_NODES\")\nif raw is not None:\n    try:\n        nodes = json.loads(raw)\n        assert isinstance(nodes, list) and all(\"host\" in n for n in nodes)\n    except (json.JSONDecodeError, AssertionError) as e:\n        raise ValueError(\n            f\"REDIS_CLUSTER_NODES must be JSON like '[{{\\\"host\\\":..,\\\"port\\\":..}}]': {e}\"\n        ) from None","typeGuard":null,"tryCatchPattern":"import json\n\ntry:\n    nodes = json.loads(raw_cluster_nodes)\nexcept json.JSONDecodeError as e:\n    raise ValueError(f\"fix REDIS_CLUSTER_NODES JSON: {e}\") from e","preventionTips":["Generate REDIS_CLUSTER_NODES programmatically (json.dumps) instead of hand-writing strings","Validate the JSON in CI for every environment file that sets it","In k8s, store the JSON in a Secret stringData field verbatim — no extra quote escaping"],"tags":["redis","cluster","environment-variables","json","configuration","cache"],"backgroundTag":"invalid-json-env-var","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}