diff --git a/pybis/src/python/pybis/dataset.py b/pybis/src/python/pybis/dataset.py
index 14268d96e1d1a7aa3ca6fb3fb435a9b6eccbf7dc..4b6fc1ac8b279aec1b68d5760cf010f611dee2c4 100755
--- a/pybis/src/python/pybis/dataset.py
+++ b/pybis/src/python/pybis/dataset.py
@@ -141,7 +141,13 @@ class DataSet(OpenBisObject):
         return
 
     def set_properties(self, properties):
-        self.openbis.update_dataset(self.permId, properties=properties)
+        """expects a dictionary of property names and their values.
+        Does not save the dataset.
+        """
+        for prop in properties.keys():
+            setattr(self.p, prop, properties[prop])
+
+    set_props = set_properties
 
     def download(self, files=None, destination=None, wait_until_finished=True, workers=10,
         linked_dataset_fileservice_url=None, content_copy_index=0):
diff --git a/pybis/src/python/pybis/experiment.py b/pybis/src/python/pybis/experiment.py
index e7a88eaadabd72cff7f608be9d2047a689adb7a9..972be3c25de39e7478a4533d1d4ebc3356c3b830 100644
--- a/pybis/src/python/pybis/experiment.py
+++ b/pybis/src/python/pybis/experiment.py
@@ -91,7 +91,10 @@ class Experiment(OpenBisObject):
         return html
 
     def set_properties(self, properties):
-        self.openbis.update_experiment(self.permId, properties=properties)
+        for prop in properties.keys():
+            setattr(self.p, prop, properties[prop])
+
+    set_props = set_properties
 
     def save(self):
         if self.is_new:
@@ -180,5 +183,3 @@ class Experiment(OpenBisObject):
 
     del_objects = del_samples # Alias
 
-
-
diff --git a/pybis/src/python/pybis/property.py b/pybis/src/python/pybis/property.py
index f9633f28bd8ef47aed353b7341a7c328ddf982b5..dc6a8928fe1e057345174ab99cd6f4dcbebab556 100644
--- a/pybis/src/python/pybis/property.py
+++ b/pybis/src/python/pybis/property.py
@@ -61,7 +61,9 @@ class PropertyHolder():
             voc = self._openbis.get_terms(name)
             value = value.upper()
             if value not in voc.df['code'].values:
-                raise ValueError("Value must be one of these terms: " + ", ".join(voc.df['code'].values))
+                raise ValueError("Value for attribute {} must be one of these terms: {}".format(
+                    name, ", ".join(voc.df['code'].values)
+                ))
         elif data_type in ('INTEGER', 'BOOLEAN', 'VARCHAR'):
             if not check_datatype(data_type, value):
                 raise ValueError("Value must be of type {}".format(data_type))
diff --git a/pybis/src/python/pybis/sample.py b/pybis/src/python/pybis/sample.py
index 500a2af1d106a46a4af65cb3f459b2a96b7c9e18..4d3bb025ae8dab83625e21c78fa0404ce698d215 100644
--- a/pybis/src/python/pybis/sample.py
+++ b/pybis/src/python/pybis/sample.py
@@ -94,7 +94,10 @@ class Sample(OpenBisObject):
         return self.a.__repr__()
 
     def set_properties(self, properties):
-        self.openbis.update_sample(self.permId, properties=properties)
+        for prop in properties.keys():
+            setattr(self.p, prop, properties[prop])
+
+    set_props = set_properties
 
     def save(self):
         props = self.p._all_props()