diff --git a/model_server/base/roiset.py b/model_server/base/roiset.py
index 403ea765a5faf8cd4511f5207c3f451c8dca836d..ce72a89b5714be64a6d8b7a20aba932ef5c71c6d 100644
--- a/model_server/base/roiset.py
+++ b/model_server/base/roiset.py
@@ -264,11 +264,11 @@ class RoiSet(object):
             patches_df = self.get_patches(**kwargs)
         return PatchStack(list(patches_df.patch))
 
-    def export_annotated_zstack(self, where, prefix='zstack', **kwargs) -> str(Path):
+    def export_annotated_zstack(self, where, prefix='zstack', **kwargs) -> str:
         annotated = InMemoryDataAccessor(draw_boxes_on_3d_image(self, **kwargs))
         fp = where / (prefix + '.tif')
         write_accessor_data_to_file(fp, annotated)
-        return str(fp)
+        return (prefix + '.tif')
 
     def get_zmask(self, mask_type='boxes'):
         """
@@ -330,13 +330,14 @@ class RoiSet(object):
             om[self.acc_obj_ids.data == roi.label] = oc
         self.object_class_maps[name] = InMemoryDataAccessor(om)
 
-    def export_dataframe(self, csv_path: Path):
+
+    def export_dataframe(self, csv_path: Path) -> str:
         csv_path.parent.mkdir(parents=True, exist_ok=True)
         self._df.drop(['expanded_slice', 'slice', 'relative_slice', 'binary_mask'], axis=1).to_csv(csv_path, index=False)
-        return csv_path
+        return csv_path.name
 
 
-    def export_patch_masks(self, where: Path, pad_to: int = None, prefix='mask', expanded=False) -> list:
+    def export_patch_masks(self, where: Path, pad_to: int = None, prefix='mask', expanded=False) -> pd.DataFrame:
         patches_df = self.get_patch_masks(pad_to=pad_to, expanded=expanded).copy()
 
         def _export_patch_mask(roi):
@@ -344,7 +345,7 @@ class RoiSet(object):
             ext = 'png'
             fname = f'{prefix}-la{roi.label:04d}-zi{roi.zi:04d}.{ext}'
             write_accessor_data_to_file(where / fname, patch)
-            return str(where / fname)
+            return fname
 
         patches_df['patch_mask_path'] = patches_df.apply(_export_patch_mask, axis=1)
         return patches_df
@@ -364,7 +365,7 @@ class RoiSet(object):
                 write_accessor_data_to_file(where / fname, resampled)
             else:
                 write_accessor_data_to_file(where / fname, patch)
-            return str(where / fname)
+            return fname
 
         patches_df['patch_path'] = patches_df.apply(_export_patch, axis=1)
         return patches_df
@@ -549,22 +550,22 @@ class RoiSet(object):
                 df_exp = self.export_patches(
                     subdir, white_channel=channel, prefix=pr, make_3d=True, expanded=True, **kp
                 )
-                record[k] = list(df_exp.patch_path)
+                record[k] = [str(Path(k) / fn) for fn in df_exp.patch_path]
             if k == 'annotated_patches_2d':
                 df_exp = self.export_patches(
                     subdir, prefix=pr, make_3d=False, white_channel=channel,
                     bounding_box_channel=1, bounding_box_linewidth=2, **kp,
                 )
-                record[k] = list(df_exp.patch_path)
+                record[k] = [str(Path(k) / fn) for fn in df_exp.patch_path]
             if k == 'patches_2d':
                 df_exp = self.export_patches(
                     subdir, white_channel=channel, prefix=pr, make_3d=False, **kp
                 )
                 self._df = self._df.join(df_exp.patch_path)
                 self._df['patch_id'] = self._df.apply(lambda _: uuid4(), axis=1)
-                record[k] = list(df_exp.patch_path)
+                record[k] = [str(Path(k) / fn) for fn in df_exp.patch_path]
             if k == 'annotated_zstacks':
-                record[k] = self.export_annotated_zstack(subdir, prefix=pr, **kp)
+                record[k] = str(Path(k) / self.export_annotated_zstack(subdir, prefix=pr, **kp))
             if k == 'object_classes':
                 for kc, acc in self.object_class_maps.items():
                     fp = subdir / kc / (pr + '.tif')
@@ -587,9 +588,11 @@ class RoiSet(object):
             pad_to=None,
             expanded=False
         )
-        self._df = self._df.join(df_exp.patch_mask_path)
-        record['dataframe'] = str(self.export_dataframe(where / 'dataframe' / (prefix + '.csv')))
-        record['tight_patch_masks'] = list(df_exp.patch_mask_path)
+        se_pa = df_exp.patch_mask_path.apply(lambda x: str(Path('tight_patch_masks') / x)).rename('tight_patch_masks')
+        self._df = self._df.join(se_pa)
+        df_fn = self.export_dataframe(where / 'dataframe' / (prefix + '.csv'))
+        record['dataframe'] = str(Path('dataframe') / df_fn)
+        record['tight_patch_masks'] = list(se_pa)
         return record
 
     @staticmethod
diff --git a/tests/test_roiset.py b/tests/test_roiset.py
index fad797404e8cbda6d552aba818c5c7a1c11817d6..da9b43db95f7a2402c309a40101a4660c26ce26b 100644
--- a/tests/test_roiset.py
+++ b/tests/test_roiset.py
@@ -102,15 +102,16 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase):
 
     def test_make_expanded_2d_patches(self):
         roiset = self._make_roi_set()
+        where = output_path / 'expanded_2d_patches'
         df_res = roiset.export_patches(
-            output_path / 'expanded_2d_patches',
+            where,
             draw_bounding_box=True,
             expanded=True,
             pad_to=256,
         )
         df = roiset.get_df()
         for f in df_res.patch_path:
-            acc = generate_file_accessor(f)
+            acc = generate_file_accessor(where / f)
             la = int(re.search(r'la([\d]+)', str(f)).group(1))
             roi_q = df.loc[df.label == la, :]
             self.assertEqual(len(roi_q), 1)
@@ -118,14 +119,15 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase):
 
     def test_make_tight_2d_patches(self):
         roiset = self._make_roi_set()
+        where = output_path / 'tight_2d_patches'
         df_res = roiset.export_patches(
-            output_path / 'tight_2d_patches',
+            where,
             draw_bounding_box=True,
             expanded=False
         )
         df = roiset.get_df()
         for f in df_res.patch_path:  # all exported files are same shape as bounding boxes in RoiSet's datatable
-            acc = generate_file_accessor(f)
+            acc = generate_file_accessor(where / f)
             la = int(re.search(r'la([\d]+)', str(f)).group(1))
             roi_q = df.loc[df.label == la, :]
             self.assertEqual(len(roi_q), 1)
@@ -134,23 +136,25 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase):
 
     def test_make_expanded_3d_patches(self):
         roiset = self._make_roi_set()
+        where = output_path / '3d_patches'
         df_res = roiset.export_patches(
-            output_path / '3d_patches',
+            where,
             make_3d=True,
             expanded=True
         )
         self.assertGreaterEqual(len(df_res), 1)
         for f in df_res.patch_path:
-            acc = generate_file_accessor(f)
+            acc = generate_file_accessor(where / f)
             self.assertGreater(acc.nz, 1)
 
 
     def test_export_annotated_zstack(self):
         roiset = self._make_roi_set()
+        where = output_path / 'annotated_zstack'
         file = roiset.export_annotated_zstack(
-            output_path / 'annotated_zstack',
+            where,
         )
-        result = generate_file_accessor(file)
+        result = generate_file_accessor(where / file)
         self.assertEqual(result.shape, roiset.acc_raw.shape)
 
     def test_flatten_image(self):
@@ -244,31 +248,34 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa
         )
 
     def test_multichannel_to_mono_2d_patches(self):
+        where = output_path / 'multichannel' / 'mono_2d_patches'
         df_res = self.roiset.export_patches(
-            output_path / 'multichannel' / 'mono_2d_patches',
+            where,
             white_channel=3,
             draw_bounding_box=True,
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(df_res.patch_path.iloc[0])
+        result = generate_file_accessor(where / df_res.patch_path.iloc[0])
         self.assertEqual(result.chroma, 1)
 
     def test_multichannnel_to_mono_2d_patches_rgb_bbox(self):
+        where = output_path / 'multichannel' / 'mono_2d_patches_rgb_bbox'
         df_res = self.roiset.export_patches(
-            output_path / 'multichannel' / 'mono_2d_patches_rgb_bbox',
+            where,
             white_channel=3,
             draw_bounding_box=True,
             bounding_box_channel=1,
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(df_res.patch_path.iloc[0])
+        result = generate_file_accessor(where / df_res.patch_path.iloc[0])
         self.assertEqual(result.chroma, 3)
 
     def test_multichannnel_to_rgb_2d_patches_bbox(self):
+        where = output_path / 'multichannel' / 'rgb_2d_patches_bbox'
         df_res = self.roiset.export_patches(
-            output_path / 'multichannel' / 'rgb_2d_patches_bbox',
+            where,
             white_channel=4,
             rgb_overlay_channels=(3, None, None),
             draw_mask=False,
@@ -278,12 +285,13 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(df_res.patch_path.iloc[0])
+        result = generate_file_accessor(where / df_res.patch_path.iloc[0])
         self.assertEqual(result.chroma, 3)
 
     def test_multichannnel_to_rgb_2d_patches_mask(self):
+        where = output_path / 'multichannel' / 'rgb_2d_patches_mask'
         df_res = self.roiset.export_patches(
-            output_path / 'multichannel' / 'rgb_2d_patches_mask',
+            where,
             white_channel=4,
             rgb_overlay_channels=(3, None, None),
             draw_mask=True,
@@ -292,12 +300,13 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(df_res.patch_path.iloc[0])
+        result = generate_file_accessor(where / df_res.patch_path.iloc[0])
         self.assertEqual(result.chroma, 3)
 
     def test_multichannnel_to_rgb_2d_patches_contour(self):
+        where = output_path / 'multichannel' / 'rgb_2d_patches_contour'
         df_res = self.roiset.export_patches(
-            output_path / 'multichannel' / 'rgb_2d_patches_contour',
+            where,
             rgb_overlay_channels=(3, None, None),
             draw_contour=True,
             contour_channel=1,
@@ -305,39 +314,42 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(df_res.patch_path.iloc[0])
+        result = generate_file_accessor(where / df_res.patch_path.iloc[0])
         self.assertEqual(result.chroma, 3)
         self.assertEqual(result.get_one_channel_data(2).data.max(), 0)  # blue channel is black
 
     def test_multichannel_to_multichannel_tif_patches(self):
+        where = output_path / 'multichannel' / 'multichannel_tif_patches'
         df_res = self.roiset.export_patches(
-            output_path / 'multichannel' / 'multichannel_tif_patches',
+            where,
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(df_res.patch_path.iloc[0])
+        result = generate_file_accessor(where / df_res.patch_path.iloc[0])
         self.assertEqual(result.chroma, 5)
         self.assertEqual(result.nz, 1)
 
     def test_multichannel_annotated_zstack(self):
+        where = output_path / 'multichannel' / 'annotated_zstack'
         file = self.roiset.export_annotated_zstack(
-            output_path / 'multichannel' / 'annotated_zstack',
+            where,
             'test_multichannel_annotated_zstack',
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(file)
+        result = generate_file_accessor(where / file)
         self.assertEqual(result.chroma, self.stack.chroma)
         self.assertEqual(result.nz, self.stack.nz)
 
     def test_export_single_channel_annotated_zstack(self):
+        where = output_path / 'annotated_zstack'
         file = self.roiset.export_annotated_zstack(
-            output_path / 'annotated_zstack',
+            where,
             channel=3,
             expanded=True,
             pad_to=256,
         )
-        result = generate_file_accessor(file)
+        result = generate_file_accessor(where / file)
         self.assertEqual(result.hw, self.roiset.acc_raw.hw)
         self.assertEqual(result.nz, self.roiset.acc_raw.nz)
         self.assertEqual(result.chroma, 1)
@@ -363,19 +375,31 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa
             'dataframe': True,
         })
 
+        where = output_path / 'run_exports'
         res = self.roiset.run_exports(
-            output_path / 'run_exports',
+            where,
             channel=3,
             prefix='test',
             params=p
         )
 
+        # test on return paths
         for k, v in res.items():
             if isinstance(v, list):
                 for f in v:
-                    self.assertTrue(Path(f).exists())
+                    self.assertFalse(Path(f).is_absolute())
+                    self.assertTrue((where / f).exists())
             else:
-                self.assertTrue(Path(v).exists())
+                self.assertFalse(Path(v).is_absolute())
+                self.assertTrue((where / v).exists())
+
+        # test on paths in CSV
+        test_df = pd.read_csv(where / res['dataframe'])
+        for c in test_df.columns:
+            if '_path' in c:
+                for f in test_df[c]:
+                    self.assertTrue((where / f).exists(), where / f)
+
 
 
 class TestRoiSetFromZmask(unittest.TestCase):