From 9f26bcc9535e3eceff953647ac0b731df1115b97 Mon Sep 17 00:00:00 2001 From: PRABHU KIRAN VANDRANKI <72809214+VANDRANKI@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:44:39 -0400 Subject: [PATCH] fix(policy_registry): preserve config-loaded policies across DB sync When sync_policies_from_db runs after load_policies, it clears _policies entirely and repopulates from the database. If the DB has no policies (e.g. config-defined policies were never persisted), all config-loaded policies are lost and every subsequent request runs without guardrails. Fix: track which policy names came from the YAML config in a new _config_policy_names set. On DB sync, start from only the config entries rather than an empty dict. DB entries with the same name still override the config version, so operator-managed DB policies continue to win. Fixes #29416 --- litellm/proxy/policy_engine/policy_registry.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/policy_engine/policy_registry.py b/litellm/proxy/policy_engine/policy_registry.py index 75017c46603..6349027125f 100644 --- a/litellm/proxy/policy_engine/policy_registry.py +++ b/litellm/proxy/policy_engine/policy_registry.py @@ -9,7 +9,7 @@ by policy_attachments (see AttachmentRegistry). import json from datetime import datetime, timezone -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple from litellm._logging import verbose_proxy_logger from litellm.types.proxy.policy_engine import ( @@ -73,6 +73,7 @@ class PolicyRegistry: self._policies: Dict[str, Policy] = {} self._policies_by_id: Dict[str, Tuple[str, Policy]] = {} self._initialized: bool = False + self._config_policy_names: Set[str] = set() def load_policies(self, policies_config: Dict[str, Any]) -> None: """ @@ -84,11 +85,13 @@ class PolicyRegistry: """ self._policies = {} self._policies_by_id = {} + self._config_policy_names = set() for policy_name, policy_data in policies_config.items(): try: policy = self._parse_policy(policy_name, policy_data) self._policies[policy_name] = policy + self._config_policy_names.add(policy_name) verbose_proxy_logger.debug(f"Loaded policy: {policy_name}") except Exception as e: verbose_proxy_logger.error( @@ -533,7 +536,13 @@ class PolicyRegistry: policy_ overrides can be resolved without DB access in the hot path. """ try: - self._policies = {} + # Preserve config-loaded policies so a DB sync with no results does not wipe them. + # DB entries for the same name will override the config version below. + self._policies = { + name: policy + for name, policy in self._policies.items() + if name in self._config_policy_names + } production = await self.get_all_policies_from_db( prisma_client, version_status="production" )