diff --git a/litellm/router.py b/litellm/router.py index c3c4172d1fd..3028bb8aeed 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -8715,6 +8715,9 @@ class Router: deployment_idx = self.model_id_to_deployment_index_map[id] try: + # Idempotent and symmetric with upsert_deployment, so a desynced + # index_map cannot leave stale wildcard credentials behind. + self._remove_deployment_from_wildcard_state(model_id=id) if deployment_idx is not None: # Pop the item from the list first item = self.model_list.pop(deployment_idx) @@ -8723,7 +8726,6 @@ class Router: self._update_deployment_indices_after_removal( model_id=id, removal_idx=deployment_idx ) - self._remove_deployment_from_wildcard_state(model_id=id) _budget_limiter = self._get_router_deployment_budget_limiter() if _budget_limiter is not None: _budget_limiter.unregister_deployment_budget(model_id=id) diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index dad81ef2edf..561c4a4a5e1 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -4448,3 +4448,24 @@ def test_remove_deployment_from_wildcard_state_is_noop_for_empty_id(): assert router.pattern_router.patterns == snapshot_patterns assert router.provider_default_deployment_ids == snapshot_ids + + +def test_delete_deployment_cleans_wildcard_state_even_when_index_is_desynced(): + """ + Symmetric with the upsert path: delete_deployment must clean wildcard + state from the model_id regardless of whether the fast-mapping index + still knows about it. Simulates index corruption / partial-failure by + removing the entry from model_id_to_deployment_index_map while leaving + pattern_router intact, then calls delete and asserts pattern_router is + cleaned. + """ + router, _ = _build_wildcard_router(api_key="key-A") + model_id = "wildcard-deployment-1" + + assert router.pattern_router.patterns + del router.model_id_to_deployment_index_map[model_id] + + router.delete_deployment(id=model_id) + + assert router.pattern_router.patterns == {} + assert model_id not in router.provider_default_deployment_ids