diff --git a/litellm/proxy/management_endpoints/ui_sso.py b/litellm/proxy/management_endpoints/ui_sso.py index cd88e9105fa..304a1b47896 100644 --- a/litellm/proxy/management_endpoints/ui_sso.py +++ b/litellm/proxy/management_endpoints/ui_sso.py @@ -2619,6 +2619,8 @@ class SSOAuthenticationHandler: # cache misses so operators can investigate the correct root cause. if _empty_value_in_dict: # Dict format was correct but code_verifier was empty/null. + # Best-effort cleanup: remove the corrupt entry before failing. + await SSOAuthenticationHandler._delete_pkce_verifier(cache_key) raise ProxyException( message=( f"PKCE verifier for state '{state}' was found in cache but " @@ -2630,6 +2632,8 @@ class SSOAuthenticationHandler: ) elif cached_data is not None: # Cache had data but in an unrecognised format (e.g. corrupt Redis value). + # Best-effort cleanup: remove the corrupt entry before failing. + await SSOAuthenticationHandler._delete_pkce_verifier(cache_key) verbose_proxy_logger.error( "PKCE verifier for state '%s' has an unrecognized format (type=%s); " "treating as a cache miss. Investigate the cached value — it may be " diff --git a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py index 5b05e16cf72..602ec6afcaf 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py +++ b/tests/test_litellm/proxy/management_endpoints/test_ui_sso.py @@ -3819,6 +3819,8 @@ class TestPKCEFunctionality: assert "cache" in exc_info.value.message.lower() or "verifier" in exc_info.value.message.lower() or "format" in exc_info.value.message.lower() assert str(exc_info.value.code) == "401" + # Strict mode should also clean up the corrupt cache entry before raising + mock_cache.async_delete_cache.assert_called_once() @pytest.mark.asyncio async def test_pkce_cache_miss_non_strict_logs_warning_and_continues(self, caplog):