diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e7f4ae..845c973 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,19 @@ XML files via lxml. See #301.
 The `odmlconversion` convenience console script has been added to convert multiple 
 previous odML version files to the latest odML version.
 
+## Changes in cloning behaviour
+
+When cloning a `Section` or a `Property` by default the id of any object is changed
+to a new UUID. The cloning methods now feature a new `keep_id` attribute. If set to
+`True`, the cloned object and any cloned children retain their original id. This
+is meant to create exact copies of Section-Property trees in different documents.
+
+## Additional validation
+
+When a document is saved, a new validation check makes sure, that a document
+contains only unique UUIDs this is required due to the introduction of creating
+clones with identical ids. 
+
 
 # Version 1.4.1
 
diff --git a/odml/base.py b/odml/base.py
index 979e54f..1c64e5b 100644
--- a/odml/base.py
+++ b/odml/base.py
@@ -581,7 +581,7 @@ class Sectionable(BaseObject):
         for i in self:
             i.clean()
 
-    def clone(self, children=True):
+    def clone(self, children=True, keep_id=False):
         """
         Clone this object recursively allowing to copy it independently
         to another document
@@ -592,7 +592,7 @@ class Sectionable(BaseObject):
         obj._sections = SmartList(BaseSection)
         if children:
             for s in self._sections:
-                obj.append(s.clone())
+                obj.append(s.clone(keep_id=keep_id))
 
         return obj
 
diff --git a/odml/property.py b/odml/property.py
index d3ea8b8..92f9245 100644
--- a/odml/property.py
+++ b/odml/property.py
@@ -407,15 +407,20 @@ class BaseProperty(base.BaseObject):
 
         return self.parent.get_path() + ":" + self.name
 
-    def clone(self):
+    def clone(self, keep_id=False):
         """
-        Clone this object to copy it independently to another document.
-        The id of the cloned object will be set to a different uuid.
+        Clone this property to copy it independently to another document.
+        By default the id of the cloned object will be set to a different uuid.
+
+        :param keep_id: If this attribute is set to True, the uuid of the
+                        object will remain unchanged.
+        :return: The cloned property
         """
         obj = super(BaseProperty, self).clone()
         obj._parent = None
         obj.value = self._value
-        obj._id = str(uuid.uuid4())
+        if not keep_id:
+            obj.new_id()
 
         return obj
 
diff --git a/odml/section.py b/odml/section.py
index b026043..3c47630 100644
--- a/odml/section.py
+++ b/odml/section.py
@@ -390,18 +390,26 @@ class BaseSection(base.Sectionable):
         else:
             raise ValueError("Can only remove sections and properties")
 
-    def clone(self, children=True):
+    def clone(self, children=True, keep_id=False):
         """
-        Clone this object recursively allowing to copy it independently
-        to another document
+        Clone this Section allowing to copy it independently
+        to another document. By default the id of any cloned
+        object will be set to a new uuid.
+
+        :param children: If True, also clone child sections and properties
+                         recursively.
+        :param keep_id: If this attribute is set to True, the uuids of the
+                        Section and all child objects will remain unchanged.
+        :return: The cloned Section.
         """
-        obj = super(BaseSection, self).clone(children)
-        obj._id = str(uuid.uuid4())
+        obj = super(BaseSection, self).clone(children, keep_id)
+        if not keep_id:
+            obj.new_id()
 
         obj._props = base.SmartList(BaseProperty)
         if children:
             for p in self._props:
-                obj.append(p.clone())
+                obj.append(p.clone(keep_id))
 
         return obj
 
diff --git a/odml/tools/odmlparser.py b/odml/tools/odmlparser.py
index 27fc9eb..00dbdec 100644
--- a/odml/tools/odmlparser.py
+++ b/odml/tools/odmlparser.py
@@ -50,7 +50,7 @@ class ODMLWriter:
         msg = ""
         for err in validation.errors:
             if err.is_error:
-                msg += "\n\t- %s %s: %s" % (err.obj, err.type, err.msg)
+                msg += "\n\t- %s %s: %s" % (err.obj, err.rank, err.msg)
         if msg != "":
             msg = "Resolve document validation errors before saving %s" % msg
             raise ParserException(msg)
diff --git a/odml/validation.py b/odml/validation.py
index 3113683..0940f33 100644
--- a/odml/validation.py
+++ b/odml/validation.py
@@ -174,7 +174,7 @@ def section_unique_ids(parent, id_map=None):
             yield i
 
         if sec.id in id_map:
-            yield ValidationError(sec, "Duplicate id in Section '%s' and '%s'" %
+            yield ValidationError(sec, "Duplicate id in Section '%s' and %s" %
                                   (sec.get_path(), id_map[sec.id]))
         else:
             id_map[sec.id] = "Section '%s'" % sec.get_path()
@@ -203,7 +203,7 @@ def property_unique_ids(section, id_map=None):
 
     for prop in section.properties:
         if prop.id in id_map:
-            yield ValidationError(prop, "Duplicate id in Property '%s' and '%s'" %
+            yield ValidationError(prop, "Duplicate id in Property '%s' and %s" %
                                   (prop.get_path(), id_map[prop.id]))
         else:
             id_map[prop.id] = "Property '%s'" % prop.get_path()

Clone id handling
Currently the `id` of a Property, Section or a Document (and their children) remains the same if the object is cloned.
- Since these ids should be used only once in a Document, by default the id of the clone and all potentially cloned children should be set to new UUIDs.
- The id attribute has to be excluded in the `baseobject.__eq__()` method.
- The clone method should also contain a `keep_id` flag which by default is `False`.  If set to `True`, the old id value is kept e.g. when attaching the exact same odML object to another Document.
- An additional validation check needs to prohibit the saving of a Document containing odML objects with identical ids.
- Currently ids of an odML entity cannot be set or changed after they have been created. Add a method  `new_id()` to assign a new UUID to an odML entity analogous to the UUID assignment in the odML Entity `__init__`.

diff --git a/test/test_property.py b/test/test_property.py
index 0877e3b..e99a20d 100644
--- a/test/test_property.py
+++ b/test/test_property.py
@@ -623,6 +623,11 @@ class TestProperty(unittest.TestCase):
         self.assertIsNotNone(prop.parent)
         self.assertIsNone(clone_prop.parent)
 
+        # Check keep_id
+        prop = Property(name="keepid")
+        clone_prop = prop.clone(True)
+        self.assertEqual(prop.id, clone_prop.id)
+
     def test_get_merged_equivalent(self):
         sec = Section(name="parent")
         mersec = Section(name="merged_section")
diff --git a/test/test_section.py b/test/test_section.py
index f4b8252..395a26d 100644
--- a/test/test_section.py
+++ b/test/test_section.py
@@ -274,6 +274,30 @@ class TestSection(unittest.TestCase):
         self.assertListEqual(clone_sec.sections, [])
         self.assertListEqual(clone_sec.properties, [])
 
+    def test_clone_keep_id(self):
+        # Check id kept in clone.
+        sec = Section(name="original")
+        clone_sec = sec.clone(keep_id=True)
+        self.assertEqual(sec, clone_sec)
+        self.assertEqual(sec.id, clone_sec.id)
+
+        # Check cloned child Sections keep their ids.
+        Section(name="sec_one", parent=sec)
+        Section(name="sec_two", parent=sec)
+        clone_sec = sec.clone(keep_id=True)
+        self.assertListEqual(sec.sections, clone_sec.sections)
+        self.assertEqual(sec.sections["sec_one"], clone_sec.sections["sec_one"])
+        self.assertEqual(sec.sections["sec_one"].id, clone_sec.sections["sec_one"].id)
+
+        # Check cloned child Properties keep their ids.
+        Property(name="prop_one", parent=sec)
+        Property(name="prop_two", parent=sec)
+        clone_sec = sec.clone(keep_id=True)
+        self.assertListEqual(sec.properties, clone_sec.properties)
+        self.assertEqual(sec.properties["prop_one"], clone_sec.properties["prop_one"])
+        self.assertEqual(sec.properties["prop_one"].id,
+                         clone_sec.properties["prop_one"].id)
+
     def test_reorder(self):
         # Test reorder of document sections
         doc = Document()
diff --git a/test/test_validation.py b/test/test_validation.py
index b2e5f7d..df616fe 100644
--- a/test/test_validation.py
+++ b/test/test_validation.py
@@ -98,3 +98,31 @@ class TestValidation(unittest.TestCase):
         p.dependency = "p2"
         res = validate(doc)
         self.assertError(res, "non-existent dependency object")
+
+    def test_property_unique_ids(self):
+        """
+        Test if identical ids in properties raise a validation error
+        """
+        doc = odml.Document()
+        sec_one = odml.Section("sec1", parent=doc)
+        sec_two = odml.Section("sec2", parent=doc)
+        prop = odml.Property("prop", parent=sec_one)
+
+        cprop = prop.clone(keep_id=True)
+        sec_two.append(cprop)
+
+        res = validate(doc)
+        self.assertError(res, "Duplicate id in Property")
+
+    def test_section_unique_ids(self):
+        """
+        Test if identical ids in sections raise a validation error.
+        """
+        doc = odml.Document()
+        sec = odml.Section("sec", parent=doc)
+
+        csec = sec.clone(keep_id=True)
+        sec.append(csec)
+
+        res = validate(doc)
+        self.assertError(res, "Duplicate id in Section")

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y libxml2-dev libxslt1-dev lib32z1-dev
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
docopt==0.6.2
exceptiongroup==1.2.2
execnet==2.1.1
iniconfig==2.1.0
isodate==0.7.2
lxml==5.3.1
-e git+https://github.com/G-Node/python-odml.git@040fe230166c642810a991d801207090fe01382a#egg=odML
packaging==24.2
pathlib==1.0.1
pluggy==1.5.0
pyparsing==3.2.3
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
PyYAML==3.13
rdflib==7.1.4
tomli==2.2.1
typing_extensions==4.13.0

name: python-odml
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - docopt==0.6.2
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - iniconfig==2.1.0
      - isodate==0.7.2
      - lxml==5.3.1
      - packaging==24.2
      - pathlib==1.0.1
      - pluggy==1.5.0
      - pyparsing==3.2.3
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - pyyaml==3.13
      - rdflib==7.1.4
      - tomli==2.2.1
      - typing-extensions==4.13.0
prefix: /opt/conda/envs/python-odml

test/test_property.py::TestProperty::test_clone test/test_section.py::TestSection::test_clone_keep_id test/test_validation.py::TestValidation::test_property_unique_ids test/test_validation.py::TestValidation::test_section_unique_ids
test/test_property.py::TestProperty::test_bool_conversion test/test_property.py::TestProperty::test_comparison test/test_property.py::TestProperty::test_dtype test/test_property.py::TestProperty::test_get_merged_equivalent test/test_property.py::TestProperty::test_get_path test/test_property.py::TestProperty::test_get_set_value test/test_property.py::TestProperty::test_id test/test_property.py::TestProperty::test_merge test/test_property.py::TestProperty::test_merge_check test/test_property.py::TestProperty::test_name test/test_property.py::TestProperty::test_new_id test/test_property.py::TestProperty::test_parent test/test_property.py::TestProperty::test_simple_attributes test/test_property.py::TestProperty::test_str_to_int_convert test/test_property.py::TestProperty::test_value test/test_property.py::TestProperty::test_value_append test/test_property.py::TestProperty::test_value_extend test/test_section.py::TestSection::test_append test/test_section.py::TestSection::test_children test/test_section.py::TestSection::test_clone test/test_section.py::TestSection::test_comparison test/test_section.py::TestSection::test_contains test/test_section.py::TestSection::test_extend test/test_section.py::TestSection::test_id test/test_section.py::TestSection::test_include test/test_section.py::TestSection::test_insert test/test_section.py::TestSection::test_link test/test_section.py::TestSection::test_merge test/test_section.py::TestSection::test_merge_check test/test_section.py::TestSection::test_name test/test_section.py::TestSection::test_new_id test/test_section.py::TestSection::test_parent test/test_section.py::TestSection::test_path test/test_section.py::TestSection::test_remove test/test_section.py::TestSection::test_reorder test/test_section.py::TestSection::test_repository test/test_section.py::TestSection::test_simple_attributes test/test_section.py::TestSection::test_unmerge test/test_validation.py::TestValidation::test_errorfree test/test_validation.py::TestValidation::test_property_in_terminology test/test_validation.py::TestValidation::test_property_values test/test_validation.py::TestValidation::test_section_in_terminology test/test_validation.py::TestValidation::test_section_type test/test_validation.py::TestValidation::test_uniques
odml/base.py odml/validation.py CHANGELOG.md odml/tools/odmlparser.py odml/section.py odml/property.py
odml/base.py odml/validation.py CHANGELOG.md odml/tools/odmlparser.py odml/section.py odml/property.py