diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index 2c31b59b795..63db2d72fe4 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -1781,11 +1781,11 @@ async def _validate_and_populate_member_user_info( Logic: 1. If both user_email and user_id are provided, verify they belong to the same user (use user_email as source of truth) 2. If only user_email is provided, populate user_id from DB - 3. If only user_id is provided, populate user_email from DB - 4. If only user_id is provided and doesn't exist, throw error + 3. If only user_id is provided, populate user_email from DB (if user exists) + 4. If only user_id is provided and doesn't exist, allow it to pass with user_email as None (will be upserted later) 5. If user_email and user_id mismatch, throw error - Returns a Member with both user_email and user_id populated. + Returns a Member with user_email and user_id populated (user_email may be None if only user_id provided and user doesn't exist). """ if member.user_email is None and member.user_id is None: raise HTTPException( @@ -1861,19 +1861,16 @@ async def _validate_and_populate_member_user_info( member.user_id = user_by_email.user_id return member - # Case 3: Only user_id provided - populate user_email from DB + # Case 3: Only user_id provided - populate user_email from DB if user exists if member.user_id is not None and member.user_email is None: user_by_id = await prisma_client.db.litellm_usertable.find_unique( where={"user_id": member.user_id} ) if user_by_id is None: - raise HTTPException( - status_code=404, - detail={ - "error": f"User with user_id '{member.user_id}' not found in database" - }, - ) + # User doesn't exist yet - allow it to pass with user_email as None + # Will be upserted later with just user_id and null email + return member # Populate user_email member.user_email = user_by_id.user_email diff --git a/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py index a5864576906..467ee3661d1 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py @@ -5559,7 +5559,8 @@ async def test_validate_and_populate_member_user_info_only_email_provided(): async def test_validate_and_populate_member_user_info_only_user_id_not_found(): """ Test _validate_and_populate_member_user_info when only user_id is provided - but the user doesn't exist in the database. Should raise HTTPException. + but the user doesn't exist in the database. Should allow it to pass with + user_email as None (will be upserted later). """ # Create member with only user_id member = Member(user_email=None, user_id="nonexistent-user", role="user") @@ -5570,17 +5571,16 @@ async def test_validate_and_populate_member_user_info_only_user_id_not_found(): # Mock find_unique to return None (user not found) mock_prisma_client.db.litellm_usertable.find_unique = AsyncMock(return_value=None) - # Call the function and expect HTTPException - with pytest.raises(HTTPException) as exc_info: - await _validate_and_populate_member_user_info( - member=member, - prisma_client=mock_prisma_client, - ) + # Call the function - should NOT raise an exception + result = await _validate_and_populate_member_user_info( + member=member, + prisma_client=mock_prisma_client, + ) - # Verify the exception details - assert exc_info.value.status_code == 404 - assert "not found" in exc_info.value.detail["error"].lower() - assert "nonexistent-user" in exc_info.value.detail["error"] + # Verify the result - should return member with user_id set and user_email as None + assert result.user_id == "nonexistent-user" + assert result.user_email is None + assert result.role == "user" # Verify find_unique was called with correct parameters mock_prisma_client.db.litellm_usertable.find_unique.assert_called_once_with(