From 1a08ac66198d3c7d6c585d6a5e7ebb9d30a87312 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Wed, 11 Mar 2026 11:01:29 -0700 Subject: [PATCH] Fix TypeError in _is_master_key when api_key is None When a request arrives without an API key, auth fails and the failure hook triggers spend tracking. _is_master_key was called with api_key=None, causing secrets.compare_digest to raise TypeError. Add a None guard for api_key matching the existing guard for _master_key. Co-Authored-By: Claude Opus 4.6 --- .../spend_tracking/spend_tracking_utils.py | 4 +-- .../test_spend_tracking_utils.py | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/spend_tracking/spend_tracking_utils.py b/litellm/proxy/spend_tracking/spend_tracking_utils.py index f381432a08..b640eaa370 100644 --- a/litellm/proxy/spend_tracking/spend_tracking_utils.py +++ b/litellm/proxy/spend_tracking/spend_tracking_utils.py @@ -47,8 +47,8 @@ def _get_max_string_length_prompt_in_db() -> int: return DEFAULT_MAX_STRING_LENGTH_PROMPT_IN_DB -def _is_master_key(api_key: str, _master_key: Optional[str]) -> bool: - if _master_key is None: +def _is_master_key(api_key: Optional[str], _master_key: Optional[str]) -> bool: + if _master_key is None or api_key is None: return False ## string comparison diff --git a/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py b/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py index 9a64e641b5..7174253538 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py +++ b/tests/test_litellm/proxy/spend_tracking/test_spend_tracking_utils.py @@ -29,6 +29,7 @@ from litellm.proxy.spend_tracking.spend_tracking_utils import ( _get_response_for_spend_logs_payload, _get_spend_logs_metadata, _get_vector_store_request_for_spend_logs_payload, + _is_master_key, _sanitize_request_body_for_spend_logs_payload, _should_store_prompts_and_responses_in_spend_logs, get_logging_payload, @@ -1440,3 +1441,30 @@ def test_get_logging_payload_includes_request_duration_ms(): ) assert payload["request_duration_ms"] == 3000 + + +class TestIsMasterKey: + """Tests for _is_master_key handling None inputs without raising TypeError.""" + + def test_none_api_key_returns_false(self): + """Regression: _is_master_key(None, 'sk-master') should return False, not raise TypeError.""" + assert _is_master_key(api_key=None, _master_key="sk-master-key") is False + + def test_none_master_key_returns_false(self): + assert _is_master_key(api_key="sk-some-key", _master_key=None) is False + + def test_both_none_returns_false(self): + assert _is_master_key(api_key=None, _master_key=None) is False + + def test_matching_key_returns_true(self): + assert _is_master_key(api_key="sk-master", _master_key="sk-master") is True + + def test_non_matching_key_returns_false(self): + assert _is_master_key(api_key="sk-other", _master_key="sk-master") is False + + def test_hashed_key_returns_true(self): + from litellm.proxy.utils import hash_token + + master = "sk-master-key-123" + hashed = hash_token(master) + assert _is_master_key(api_key=hashed, _master_key=master) is True