fix(proxy): tolerate missing team_id in responses id decrypt

_decrypt_response_id required parts[2] after only checking len(parts) >= 2,
so a two-segment managed id (response_id + user_id, no team) raised
IndexError and broke follow-up /v1/responses calls.

Treat team_id as optional when the third segment is absent.

Fixes #43493
This commit is contained in:
Tanvir Alam 2026-09-28 00:34:58 -04:00
parent 74cad08997
commit de7ebd871b
2 changed files with 27 additions and 4 deletions

View file

@ -244,7 +244,9 @@ class ResponsesIDSecurity(CustomLogger):
return response_id, None, None
if decrypted_value.startswith(SpecialEnums.LITELM_MANAGED_FILE_ID_PREFIX.value):
# Expected format: "litellm_proxy:responses_api:response_id:{response_id};user_id:{user_id}"
# Formats:
# - "litellm_proxy:responses_api:response_id:{id};user_id:{user};team_id:{team}"
# - "litellm_proxy:responses_api:response_id:{id};user_id:{user}" (legacy / no team)
parts: Final = decrypted_value.split(";")
if len(parts) >= 2:
@ -256,9 +258,11 @@ class ResponsesIDSecurity(CustomLogger):
user_id_part: Final = parts[1]
user_id: Final = user_id_part.split("user_id:")[-1]
# Extract team_id from "team_id:{team_id}"
team_id_part: Final = parts[2]
team_id: Final = team_id_part.split("team_id:")[-1]
# Team segment is optional; older two-segment ids omit it.
team_id: str | None = None
if len(parts) >= 3:
team_id_part: Final = parts[2]
team_id = team_id_part.split("team_id:")[-1]
return original_response_id, user_id, team_id
else:

View file

@ -113,6 +113,25 @@ class TestDecryptResponseId:
assert team_id is None
def test_decrypt_response_id_two_segment_no_team(self, responses_id_security):
"""Two-segment managed ids (no team_id) must not IndexError on parts[2]."""
import litellm.proxy.hooks.responses_id_security as responses_module
with patch.object(responses_module, "decrypt_value_helper") as mock_decrypt:
mock_decrypt.return_value = (
f"{SpecialEnums.LITELM_MANAGED_FILE_ID_PREFIX.value}"
"response_id:resp_abc;user_id:user-1"
)
original_id, user_id, team_id = responses_id_security._decrypt_response_id(
"resp_encrypted_two_segment"
)
assert original_id == "resp_abc"
assert user_id == "user-1"
assert team_id is None
class TestCheckUserAccessToResponseId:
"""Test check_user_access_to_response_id function"""