BerriAI/litellm · error · RuntimeError
Cannot migrate covered tables: no salt key / master key is s
Error message
Cannot migrate covered tables: no salt key / master key is set. Set LITELLM_SALT_KEY before migrating.
What it means
Raised by LiteLLM's credential-migration flow (litellm/proxy/management_endpoints/credential_migration.py) when the pre-flight _get_salt_key() returns None: the proxy process has neither LITELLM_SALT_KEY set nor a master key to fall back to (encrypt_decrypt_utils._get_salt_key checks the env var, then master_key). Migration re-encrypts stored provider credentials by calling _rotate_master_key with the same key (algorithm-only switch), so a key must exist before any rotation runs. The RuntimeError aborts before a single table is touched.
Source
Thrown at litellm/proxy/management_endpoints/credential_migration.py:578
"""Re-encrypt the tables already covered by ``_rotate_master_key`` (model
table, credentials, MCP credential/env tables, config environment_variables)
by running that orchestrator in *same-key* mode. With the AES gate on, the
re-encrypt writes land in ``v2:`` format.
``_rotate_master_key`` returns no counts, so we bracket it with read-only
scans: the pre-scan's legacy total minus the post-scan's gives the number
actually migrated per location, and the post-scan supplies the residual /
already-v2 / scanned figures. Returns one report per covered location.
"""
from litellm.proxy.management_endpoints.key_management_endpoints import (
_rotate_master_key,
)
pre: Final = {r.location: r for r in await _scan_covered_tables(prisma_client)}
current_key: Final = _get_salt_key()
if current_key is None:
raise RuntimeError(
"Cannot migrate covered tables: no salt key / master key is set. Set LITELLM_SALT_KEY before migrating."
)
await _rotate_master_key(
prisma_client=cast("PrismaClient", prisma_client),
user_api_key_dict=cast("UserAPIKeyAuth", user_api_key_dict),
current_master_key=current_key,
new_master_key=current_key, # same key, algorithm-only switch
)
post: Final = await _scan_covered_tables(prisma_client)
for post_report in post:
pre_report = pre.get(post_report.location)
pre_legacy = pre_report.legacy if pre_report else 0
# Everything that was legacy before and is no longer legacy now was
# converted this run.
post_report.migrated = max(0, pre_legacy - post_report.legacy)
return post
View on GitHub (pinned to 77b7c6c40c)
Solutions
- Export LITELLM_SALT_KEY in the proxy process environment (deployment env vars, docker -e, or systemd Environment=) and restart the proxy
- Alternatively ensure a master key (LITELLM_MASTER_KEY / general_settings.master_key) is configured, since _get_salt_key() falls back to it
- Verify inside the same container/process that runs the migration: printenv LITELLM_SALT_KEY must be non-empty
- Re-run the migration endpoint and confirm the pre/post scan reports are returned
Example fix
# before podman run -p 4000:4000 litellm/litellm --config /app/config.yaml # no salt key -> migration raises RuntimeError # after podman run -p 4000:4000 -e LITELLM_SALT_KEY=sk-salt-... litellm/litellm --config /app/config.yaml
Defensive patterns
Strategy: validation
Validate before calling
import os
def credential_migration_ready() -> bool:
return bool(os.getenv("LITELLM_SALT_KEY") or os.getenv("LITELLM_MASTER_KEY"))
if not credential_migration_ready():
raise SystemExit("Set LITELLM_SALT_KEY before running credential migration") Try / catch
try:
report = await migrate_covered_tables(prisma_client, user_api_key_dict)
except RuntimeError as e:
if "no salt key" in str(e):
# environment problem, not a data problem: fix env and re-run once
raise SystemExit("LITELLM_SALT_KEY missing in this process") from e
raise Prevention
- Pass LITELLM_SALT_KEY explicitly in deployment manifests instead of relying on interactive shell exports
- Add a startup readiness check that fails fast when the salt key is missing, before any migration job is scheduled
- Run migration jobs with the same secret-injection mechanism as the proxy container
When it happens
Trigger: Invoking the covered-tables credential migration endpoint while the proxy process has no LITELLM_SALT_KEY exported and no master key configured; running the migration from a new pod, CI job, or shell that did not inherit the proxy's secret environment.
Common situations: Ops schedules a credentials re-encryption after upgrading LiteLLM, but the Kubernetes deployment / Docker env is missing LITELLM_SALT_KEY; the key lives in a .env file the proxy never loads; the migration is invoked from a different container than the one holding the secret.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- Invalid hash key. Hash key={hashed_token}. Decrypted token={
- Failed to retrieve file {file_id} from provider: {str(e)}
- API key is required for OpenAI image variations
- API key is required for Topaz image variations
- Missing webhook_url from environment
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/174ebf2e5fd727ef.
Report an issue: GitHub.