{"record":{"id":"70f94a58df9289a6","repo":"BerriAI/litellm","slug":"encryption-migration-requires-general-settings-enc","errorCode":null,"errorMessage":"Encryption migration requires general_settings.encryption_algorithm: 'aes-256-gcm'. Current value: {algo!r}. Set it before migrating so re-encrypted values are written in the AES-256-GCM format.","messagePattern":"Encryption migration requires general_settings\\.encryption_algorithm: 'aes-256-gcm'\\. Current value: (.+?)\\. Set it before migrating so re-encrypted values are written in the AES-256-GCM format\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/management_endpoints/credential_migration.py","lineNumber":185,"sourceCode":"    for k in sensitive_keys:\n        v = out.get(k)\n        if v is None:\n            continue\n        out[k] = reencrypt_value(v, key=k)\n    return out\n\n\ndef _assert_aes_gate_enabled() -> None:\n    \"\"\"Fail fast if the AES algorithm gate is not enabled.\n\n    Running the migration with the gate off would decrypt then re-encrypt right\n    back into the legacy format — a no-op that silently fails the migration.\n    \"\"\"\n    from litellm.proxy.proxy_server import general_settings\n\n    algo: Final = general_settings.get(_ENCRYPTION_ALGORITHM_SETTING)\n    if not (isinstance(algo, str) and algo.lower() == _ALGO_AES_GCM):\n        raise RuntimeError(\n            \"Encryption migration requires general_settings.encryption_algorithm: \"\n            f\"'{_ALGO_AES_GCM}'. Current value: {algo!r}. Set it before migrating \"\n            \"so re-encrypted values are written in the AES-256-GCM format.\"\n        )\n\n\n# ---------------------------------------------------------------------------\n# Walkers for the locations with no pre-existing rotation path.\n# Each walker delegates the structural transform to the existing, tested helper\n# for that table and only adds the per-row re-encrypt + commit + counters.\n# ---------------------------------------------------------------------------\n\n\nasync def _migrate_config_settings_row(\n    prisma_client: object,\n    param_name: str,\n    sensitive_fields: list[str],\n    dry_run: bool,","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/credential_migration.py#L167-L203","documentation":"RuntimeError raised by _assert_aes_gate_enabled() before the credential re-encryption migration runs: it reads general_settings.encryption_algorithm from the running proxy config and requires the exact value 'aes-256-gcm' (case-insensitive). The guard exists because running the migration with the AES gate off would decrypt legacy nacl ciphertext and immediately re-encrypt it back into the legacy format — a silent no-op migration — so it fails fast instead.","triggerScenarios":"Invoking the encryption migration (the credential_migration module's migrate/check flows) while general_settings.encryption_algorithm is unset (None), set to the legacy default, or misspelled; setting the key somewhere the running proxy doesn't read (wrong config file, env not loaded).","commonSituations":"Compliance-driven migrations from XSalsa20-Poly1305 to AES-256-GCM where the operator forgot the prerequisite config step; setting encryption_algorithm after the proxy started without restarting; YAML indentation putting the key outside general_settings.","solutions":["In the proxy config, set general_settings.encryption_algorithm: aes-256-gcm and restart/reload so general_settings in the running server carries it.","Re-run the migration; the guard is a pure precondition — no state was touched when it raised.","Keep the same master key: the migration re-encrypts under the same derived key, so do not rotate the key at the same time.","After migration, verify with the read-only check_encryption scan that residual legacy == 0."],"exampleFix":"# before (config.yaml)\nlitellm_settings:\n  encryption_algorithm: aes-256-gcm   # wrong section -> RuntimeError(None)\n\n# after\ngeneral_settings:\n  encryption_algorithm: aes-256-gcm","handlingStrategy":"validation","validationCode":"import yaml, requests\n\ncfg = yaml.safe_load(open(\"config.yaml\"))\nalgo = cfg.get(\"general_settings\", {}).get(\"encryption_algorithm\", \"\")\nif str(algo).lower() != \"aes-256-gcm\":\n    raise SystemExit(\n        \"Set general_settings.encryption_algorithm: aes-256-gcm and restart the proxy \"\n        \"before running the encryption migration.\"\n    )","typeGuard":null,"tryCatchPattern":"try:\n    run_encryption_migration(prisma_client)\nexcept RuntimeError as e:\n    if \"encryption_algorithm\" in str(e):\n        # precondition failure: nothing was migrated; fix config and restart proxy, then re-run\n        set_general_setting(\"encryption_algorithm\", \"aes-256-gcm\")\n        restart_proxy()\n        run_encryption_migration(prisma_client)  # idempotent, safe to re-run\n    else:\n        raise","preventionTips":["Treat general_settings.encryption_algorithm: aes-256-gcm as a checklist precondition of the migration runbook.","Restart (or hot-reload) the proxy after changing the setting so its in-memory general_settings is updated.","Run the read-only check_encryption scan before and after; residual legacy == 0 is your attestation.","Never rotate the master key and the algorithm in the same operation."],"tags":["litellm-proxy","encryption","migration","config","security"],"backgroundTag":"missing-config-setting","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}