diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81b19e4c41504ef350fb638f10b9195564c17109..e109ba5a37dcccabcaf2b341212def848e676a99 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,13 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added ###
 
+- It is possible now to supply a password for caosdb_admin on the command line and also activate the user directly using "-c".
 * Added examples for complex data models to documentation
 * extended apiutils with `resolve_reference(Property)`
 * is_reference function for Properties
 
 ### Changed ###
 
-- It is possible now to supply a password for caosdb_admin on the command line and also activate the user directly using "-c".
+* Retrievals of entities where the class does not match the entity role raise
+  a ValueError now. See
+  [here](https://gitlab.indiscale.com/caosdb/src/caosdb-pylib/-/issues/66) for more
+  information. Updating a role is now being done by setting the `Entity.role`
+  to the new role (as string).
 * Entity.add_property and Entity.add_parent do not accept `**kwargs`-style
   keywords anymore. Formerly known keywords have been refactored into named
   parameters.
@@ -38,6 +43,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed ###
 
+* #60 Unintuitive behavior of `Entity.role` after a `Entity(id).retrieve()`
+  Originally the role was `None`. The correct role is present now.
 * #53 Documentation of inheritance
 * #38 Dependencies in chunk-deletion of containers
 
diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py
index a6d085c170a17722f53f51845e57e9411cb6a12b..9a168a7f83c13d2e2ae1bd7bed857dedc364fa7d 100644
--- a/src/caosdb/common/models.py
+++ b/src/caosdb/common/models.py
@@ -130,7 +130,10 @@ class Entity(object):
 
     @role.setter
     def role(self, role):
-        self.__role = role
+        if role is not None and role.lower() == "entity":
+            self.__role = None
+        else:
+            self.__role = role
 
     @property
     def size(self):
@@ -390,6 +393,12 @@ class Entity(object):
         abstract_property = None
 
         if isinstance(property, Entity):
+            if property.role is not None and property.role.lower() in ["record", "file"]:
+                raise ValueError("The property parameter is a {0}. This "
+                                 "is very unusual and probably not what you "
+                                 "want. Otherwise, construct a property from "
+                                 "a {0} using the Property class and add "
+                                 "that to this entity.".format(property.role))
             abstract_property = property
         elif isinstance(property, int):
             if pid is not None:
@@ -848,7 +857,9 @@ class Entity(object):
         """
 
         if xml is None:
-            xml = etree.Element("Entity")
+            # use role as xml tag name, fall-back to "Entity"
+            elem_tag = "Entity" if self.role is None else self.role
+            xml = etree.Element(elem_tag)
         assert isinstance(xml, etree._Element)
 
         # unwrap wrapped entity
@@ -967,6 +978,8 @@ class Entity(object):
         @param elem: the xml element
         """
 
+        if isinstance(entity, Entity):
+            entity.role = elem.tag
         entity._cuid = elem.get("cuid")
         entity.id = elem.get("id")  # @ReservedAssignment
         entity.name = elem.get("name")
@@ -1281,6 +1294,7 @@ class QueryTemplate():
     def __init__(self, id=None, name=None, query=None, description=None):  # @ReservedAssignment
 
         self.id = (int(id) if id is not None else None)
+        self.role = "QueryTemplate"
         self.name = name
         self.description = description
         self.query = query
@@ -2313,15 +2327,45 @@ class _Messages(dict):
 
 
 def _basic_sync(e_local, e_remote):
+    '''Copy all state from a one entity to another.
+
+    This method is used to syncronize an entity with a remote (i.e. a newly
+    retrieved) one.
+
+    Any entity state of the local one will be overriden.
+
+    Parameters
+    ----------
+    e_local : Entity
+        Destination of the copy.
+    e_local : Entity
+        Source of the copy.
+
+
+    Returns
+    -------
+    e_local : Entity
+        The syncronized entity.
+        '''
     if e_local is None or e_remote is None:
         return None
+    if e_local.role is None:
+        e_local.role = e_remote.role
+    elif e_remote.role is not None and not e_local.role.lower() == e_remote.role.lower():
+        raise ValueError("The resulting entity had a different role ({0}) "
+                         "than the local one ({1}). This probably means, that "
+                         "the entity was intialized with a wrong class "
+                         "by this client or it has changed in the past and "
+                         "this client did't know about it yet.".format(
+                             e_remote.role, e_local.role))
+
     e_local.id = e_remote.id
     e_local.name = e_remote.name
     e_local.description = e_remote.description
     e_local.path = e_remote.path
     e_local._checksum = e_remote._checksum
     e_local._size = e_remote._size
-    e_local.datatype = e_remote.datatype  # @ReservedAssignment
+    e_local.datatype = e_remote.datatype
     e_local.unit = e_remote.unit
     e_local.value = e_remote.value
     e_local.properties = e_remote.properties
@@ -4215,7 +4259,7 @@ def _evaluate_and_add_error(parent_error, ent):
 
     Parameters:
     -----------
-    parent_error : TrancactionError
+    parent_error : TransactionError
         Parent error to which the new exception will be attached. This
         exception will be a direct child.
     ent : Entity
diff --git a/unittests/test_add_property.py b/unittests/test_add_property.py
index 5bae6c219732f0170f5c351eae58148c9d3d065a..0d3183b4c0ca5517ecea68d0e49bbf335bb2a13e 100644
--- a/unittests/test_add_property.py
+++ b/unittests/test_add_property.py
@@ -264,3 +264,33 @@ def test_add_list_of_entitities():
     for e in rec.get_property("listOfEntities").value:
         assert i == e.id
         i += 1
+
+
+def test_add_property_with_wrong_role():
+    entity = db.Entity()
+
+    r = db.Record()
+    rt = db.RecordType()
+    p = db.Property()
+    f = db.File()
+    e = db.Entity()
+
+    entity.add_property(rt)
+    entity.add_property(p)
+    entity.add_property(e)
+
+    with raises(ValueError) as cm:
+        entity.add_property(r)
+    assert cm.value.args[0] == ("The property parameter is a Record. This is "
+                                "very unusual and probably not what you want. "
+                                "Otherwise, construct a property from a "
+                                "Record using the Property class and add that "
+                                "to this entity.")
+
+    with raises(ValueError) as cm:
+        entity.add_property(f)
+    assert cm.value.args[0] == ("The property parameter is a File. This is "
+                                "very unusual and probably not what you want. "
+                                "Otherwise, construct a property from a File "
+                                "using the Property class and add that to "
+                                "this entity.")
diff --git a/unittests/test_entity.py b/unittests/test_entity.py
index e98dfbef5b6b5e5f691e8aecc2fa7d4a86991452..1e88702ac016d7dcfdf00919dd0f93b5d3345e00 100644
--- a/unittests/test_entity.py
+++ b/unittests/test_entity.py
@@ -24,6 +24,7 @@
 """Tests for the Entity class."""
 # pylint: disable=missing-docstring
 import unittest
+from lxml import etree
 
 from caosdb import (INTEGER, Entity, Property, Record, RecordType,
                     configure_connection)
@@ -42,17 +43,54 @@ class TestEntity(unittest.TestCase):
     def test_instance_variables(self):
         entity = Entity()
         self.assertTrue(hasattr(entity, "role"))
+        self.assertIsNone(entity.role)
         self.assertTrue(hasattr(entity, "id"))
         self.assertTrue(hasattr(entity, "name"))
         self.assertTrue(hasattr(entity, "description"))
         self.assertTrue(hasattr(entity, "parents"))
         self.assertTrue(hasattr(entity, "properties"))
 
-    def test_role(self):
+    def test_entity_role_1(self):
         entity = Entity(role="TestRole")
         self.assertEqual(entity.role, "TestRole")
         entity.role = "TestRole2"
         self.assertEqual(entity.role, "TestRole2")
 
-    def test_instanciation(self):
+    def test_entity_role_2(self):
+        entity = Entity()
+
+        self.assertIsNone(entity.role)
+        self.assertEqual(entity.to_xml().tag, "Entity")
+
+        entity.role = "Record"
+        self.assertEqual(entity.role, "Record")
+        self.assertEqual(entity.to_xml().tag, "Record")
+
+    def test_recordtype_role(self):
+        entity = RecordType()
+
+        self.assertEqual(entity.role, "RecordType")
+        self.assertEqual(entity.to_xml().tag, "RecordType")
+
+    def test_property_role(self):
+        entity = Property()
+
+        self.assertEqual(entity.role, "Property")
+        self.assertEqual(entity.to_xml().tag, "Property")
+
+    def test_instantiation(self):
         self.assertRaises(Exception, Entity())
+
+    def test_parse_role(self):
+        """During parsing, the role of an entity is set explicitely. All other
+        classes use the class name as a "natural" value for the role property.
+        """
+        parser = etree.XMLParser(remove_comments=True)
+        entity = Entity._from_xml(Entity(),
+                                  etree.parse("unittests/test_record.xml",
+                                              parser).getroot())
+
+        self.assertEqual(entity.role, "Record")
+        # test whether the __role property of this object has explicitely been
+        # set.
+        self.assertEqual(getattr(entity, "_Entity__role"), "Record")