diff --git a/model_server/base/api.py b/model_server/base/api.py
index 9c69038c631dd19309e02c0d9d39a4f52aab5c10..3549633db13fecea94fbb2dd77fc1cb50922ef07 100644
--- a/model_server/base/api.py
+++ b/model_server/base/api.py
@@ -262,5 +262,4 @@ def list_tasks() -> Dict[str, TaskInfo]:
 
 @app.get('/phenobase/bounding_box')
 def get_phenobase_bounding_boxes() -> list:
-    # TODO: exception when casting this as list
     return session.phenobase.list_bounding_boxes()
\ No newline at end of file
diff --git a/model_server/base/roiset.py b/model_server/base/roiset.py
index 696922b9bdfa4b5dad3c6b3f530395af18a605f4..244acc651d6125d86d64c87f10e7e9af99da8850 100644
--- a/model_server/base/roiset.py
+++ b/model_server/base/roiset.py
@@ -489,6 +489,9 @@ def insert_level(df: pd.DataFrame, name: str):
         ],
     )
 
+def read_roiset_df(csv_path: Path) -> pd.DataFrame:
+    return pd.read_csv(csv_path, header=[0, 1], index_col=0)
+
 
 class RoiSet(object):
 
@@ -1287,7 +1290,6 @@ class RoiSet(object):
     def acc_obj_ids(self):
         return make_object_ids_from_df(self._df, self.acc_raw.shape_dict)
 
-    # TODO: move to dedicated CSV reader in tests and scripts
 
     @classmethod
     def deserialize(cls, acc_raw: GenericImageDataAccessor, where: Path, prefix='roiset') -> Self:
@@ -1299,7 +1301,7 @@ class RoiSet(object):
         :param prefix: starting prefix of patch mask filenames
         :return: RoiSet object
         """
-        df = pd.read_csv(where / 'dataframe' / (prefix + '.csv'), header=[0, 1], index_col=0)
+        df = read_roiset_df(where / 'dataframe' / (prefix + '.csv'))
         df.index.name = 'label'
         pa_masks = where / 'tight_patch_masks'
         is_3d = is_df_3d(df)
diff --git a/tests/base/test_roiset.py b/tests/base/test_roiset.py
index 07acffa5cc8a686ee69f54e1b1ea210e6d586e11..fc017505e8a6b30e38def265b60e409de3aa22ca 100644
--- a/tests/base/test_roiset.py
+++ b/tests/base/test_roiset.py
@@ -7,7 +7,7 @@ from pathlib import Path
 import pandas as pd
 
 from model_server.base.process import smooth
-from model_server.base.roiset import filter_df_overlap_bbox, filter_df_overlap_seg, IntensityThresholdInstanceMaskSegmentationModel, RoiSet, RoiSetExportParams, RoiSetMetaParams
+from model_server.base.roiset import filter_df_overlap_bbox, filter_df_overlap_seg, IntensityThresholdInstanceMaskSegmentationModel, read_roiset_df, RoiSet, RoiSetExportParams, RoiSetMetaParams
 from model_server.base.accessors import generate_file_accessor, InMemoryDataAccessor, PatchStack, write_accessor_data_to_file
 import model_server.conf.testing as conf
 from model_server.conf.testing import DummyInstanceMaskSegmentationModel
@@ -661,7 +661,7 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa
                 self.assertTrue((where / v).exists())
 
         # test on paths in CSV
-        test_df = pd.read_csv(where / res['dataframe'], header=[0, 1], index_col=0)
+        test_df = read_roiset_df(where / res['dataframe'])
         for c in ['tight_patch_masks_path', 'patches_2d_path', 'patches_2d_annotated_path']:
             self.assertTrue(c in test_df.patches.columns)
             for f in test_df.patches[c]:
@@ -818,7 +818,7 @@ class TestRoiSetSerialization(unittest.TestCase):
         ref_roiset.serialize(where_ser, prefix='ref')
         where_df = where_ser / 'dataframe' / 'ref.csv'
         self.assertTrue(where_df.exists())
-        df_test = pd.read_csv(where_df, header=[0, 1], index_col=0)
+        df_test = read_roiset_df(where_df)
 
         # check that patches are correct size
         where_patch_masks = where_ser / 'tight_patch_masks'