diff --git a/src/viralhostrange/viralhostrangedb/forms.py b/src/viralhostrange/viralhostrangedb/forms.py
index e71b26bce87333ab6be0e1fc6b38eeb1221a320a..0f15a240f75f43891f408956323a12bace1fea01 100644
--- a/src/viralhostrange/viralhostrangedb/forms.py
+++ b/src/viralhostrange/viralhostrangedb/forms.py
@@ -7,14 +7,14 @@ import string
 
 import pandas as pd
 from basetheme_bootstrap.user_preferences_utils import get_user_preferences_for_user
-from crispy_forms import helper as  crispy_forms_helper
+from crispy_forms import helper as crispy_forms_helper
 from crispy_forms import layout as crispy_forms_layout
 from crispy_forms.helper import FormHelper
 from crispy_forms.layout import HTML, Layout, Row, Column
 from django import forms
 from django.conf import settings
 from django.contrib.auth import get_user_model
-from django.core.exceptions import FieldDoesNotExist
+from django.core.exceptions import FieldDoesNotExist, ValidationError
 from django.db.models import Q, Min, Max
 from django.forms import widgets
 from django.utils import timezone
@@ -483,6 +483,10 @@ class AutoMergeSplitUpdateFormSet(forms.BaseModelFormSet):
                 obj.save()
                 # And use this copy as its alter_ego
                 alter_ego = obj
+            elif alter_ego.data_source.filter(pk=self.__data_source.pk).exists():
+                raise ValidationError(
+                    mark_safe(ugettext("Duplicated entry <b>%s</b> have been found but is not allowed.")
+                              % alter_ego.explicit_name_html))
 
             # remove the old instance from the current data_source
             old_o.data_source.remove(self.__data_source)
diff --git a/src/viralhostrange/viralhostrangedb/tests/test_views_data_source.py b/src/viralhostrange/viralhostrangedb/tests/test_views_data_source.py
index 66940cb2b66a5b08a7a479d2a0a55ed54161f0fd..432282f5ca64c218f6deb129f29419dca6b87514 100644
--- a/src/viralhostrange/viralhostrangedb/tests/test_views_data_source.py
+++ b/src/viralhostrange/viralhostrangedb/tests/test_views_data_source.py
@@ -190,7 +190,7 @@ class DataSourceUpdateViewTestCase(ViewTestCase):
         form_data = dict(
             name=self.public_data_source_of_user.name,
             life_domain="bacteria",
-            description="a "*20,
+            description="a " * 20,
             public=True,
             allowed_users_editor=
             "\n".join(["%s %s;%i;%s" % (gu.user.last_name, gu.user.first_name, gu.user.pk, gu.can_write)
@@ -1405,6 +1405,41 @@ class DataSourceVirusOrHostUpdateTestCase(ViewTestCase):
 
         self.assertEqual(initial_overall_count, getattr(data_source, entry_set).first().__class__.objects.count())
 
+    def test_host_duplication_fails(self):
+        url = 'viralhostrangedb:data-source-host-update'
+        self.actual_test_duplication_fails(url, "host_set", self.private_data_source_of_toto)
+
+    def test_virus_duplication_fails(self):
+        url = 'viralhostrangedb:data-source-virus-update'
+        self.actual_test_duplication_fails(url, "virus_set", self.private_data_source_of_toto)
+
+    def actual_test_duplication_fails(self, url, entry_set, data_source):
+        is_virus = entry_set.startswith("virus")
+        url = reverse(url, args=[data_source.pk])
+        self.client.force_login(self.toto)
+        count = getattr(data_source, entry_set).order_by("id").count()
+
+        self.assertFalse(getattr(data_source, entry_set).filter(name="Tralala").exists())
+
+        form_data = {
+            "form-TOTAL_FORMS": count,
+            "form-INITIAL_FORMS": count,
+            "form-MIN_NUM_FORMS": 0,
+            "form-MAX_NUM_FORMS": 1000,
+        }
+        for i, v in enumerate(getattr(data_source, entry_set).order_by("id")):
+            form_data["form-%i-name" % i] = "Tralala"
+            form_data["form-%i-id" % i] = v.id
+            form_data["form-%i-identifier" % i] = ""
+            if is_virus:
+                form_data["form-%i-her_identifier" % i] = ""
+
+        response = self.client.post(url, form_data)
+        self.assertEqual(len(list(response.context['messages'])), 1)
+        self.assertEqual(response.status_code, 200)
+
+        self.assertFalse(getattr(data_source, entry_set).filter(name="Tralala").exists())
+
     def test_delete_not_possible(self):
         url = reverse('viralhostrangedb:data-source-virus-update', args=[self.private_data_source_of_toto.pk])
         entry_set = "virus_set"
diff --git a/src/viralhostrange/viralhostrangedb/views.py b/src/viralhostrange/viralhostrangedb/views.py
index be3f1e5ccfb68cca65fe59b44141395c81623a89..d13c6699524e4be7074fba2d7314b90b91bb3fef 100644
--- a/src/viralhostrange/viralhostrangedb/views.py
+++ b/src/viralhostrange/viralhostrangedb/views.py
@@ -19,7 +19,7 @@ from django.contrib.auth.decorators import login_required
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.contenttypes.models import ContentType
 from django.core import signing
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, ValidationError
 from django.core.files import storage
 from django.core.files.temp import NamedTemporaryFile
 from django.core.mail import EmailMultiAlternatives
@@ -852,6 +852,8 @@ def data_source_entry_update(request, pk, entry_class, formset_class, title, for
                             ],
                             altered_data=altered_data,
                         )
+            except ValidationError as e:
+                messages.error(request=request, message=e.message)
             except Exception as e:
                 messages.error(request=request, message=e)
             else: