From 921bfe6c70ca3aeb5cdfe773a8df32844eddbf49 Mon Sep 17 00:00:00 2001 From: Florent Chenebault Date: Fri, 29 May 2026 12:04:39 +0200 Subject: [PATCH] fix(proxy): add default=None to LiteLLM_TeamMembership.litellm_budget_table In Pydantic v2, Optional[T] without a default is a required field. Any row with budget_id=null triggered a validation error and returned 401. --- litellm/proxy/_types.py | 4 +++- tests/test_litellm/proxy/test_proxy_types.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 522e85632dc5..e02ed879ba83 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -3892,7 +3892,9 @@ class LiteLLM_TeamMembership(LiteLLMPydanticObjectBase): # Union so Pydantic picks Full when data has server-managed fields # (/team/info) and Base when callers/tests construct with only # user-settable fields. - litellm_budget_table: Optional[Union[LiteLLM_BudgetTableFull, LiteLLM_BudgetTable]] + litellm_budget_table: Optional[ + Union[LiteLLM_BudgetTableFull, LiteLLM_BudgetTable] + ] = None def safe_get_team_member_rpm_limit(self) -> Optional[int]: if self.litellm_budget_table is not None: diff --git a/tests/test_litellm/proxy/test_proxy_types.py b/tests/test_litellm/proxy/test_proxy_types.py index 0fa86798999f..cae232d247fc 100644 --- a/tests/test_litellm/proxy/test_proxy_types.py +++ b/tests/test_litellm/proxy/test_proxy_types.py @@ -47,6 +47,24 @@ def test_audit_log_masking(): assert json_before_value["key"] == "sk-1*****7890" +def test_team_membership_null_budget_table(): + """ + Regression test for: LiteLLM_TeamMembership.litellm_budget_table missing = None. + In Pydantic v2, Optional[T] without a default is required; rows with budget_id=null + raised a validation error and returned 401. + Related: https://github.com/BerriAI/litellm/issues/28689 + """ + from litellm.proxy._types import LiteLLM_TeamMembership + + membership = LiteLLM_TeamMembership(user_id="u1", team_id="t1") + assert membership.litellm_budget_table is None + + membership_explicit = LiteLLM_TeamMembership( + user_id="u1", team_id="t1", litellm_budget_table=None + ) + assert membership_explicit.litellm_budget_table is None + + def test_internal_jobs_user_has_proxy_admin_role(): """ Test that the internal jobs system user has PROXY_ADMIN role.