diff --git a/hgitaly/VERSION b/hgitaly/VERSION
index 46369243c1c57f312002498eea962bbf26ba9ead_aGdpdGFseS9WRVJTSU9O..f7c06a8948e0cd1553ddada31ecf37c4776a6014_aGdpdGFseS9WRVJTSU9O 100644
--- a/hgitaly/VERSION
+++ b/hgitaly/VERSION
@@ -1,1 +1,1 @@
-0.20.1dev0
+0.21.1dev0
diff --git a/hgitaly/errors.py b/hgitaly/errors.py
index 46369243c1c57f312002498eea962bbf26ba9ead_aGdpdGFseS9lcnJvcnMucHk=..f7c06a8948e0cd1553ddada31ecf37c4776a6014_aGdpdGFseS9lcnJvcnMucHk= 100644
--- a/hgitaly/errors.py
+++ b/hgitaly/errors.py
@@ -90,8 +90,7 @@
     return response_cls()
 
 
-def already_exists(context, response_cls, message,
-                   log_level=logging.WARN):  # pragma no cover
+def already_exists(context, response_cls, message, log_level=logging.WARN):
     """Return grpc UNKNOWN code with message.
 
     Similar to :func:`not_found`, except for the default ``log_level``
diff --git a/hgitaly/service/repository.py b/hgitaly/service/repository.py
index 46369243c1c57f312002498eea962bbf26ba9ead_aGdpdGFseS9zZXJ2aWNlL3JlcG9zaXRvcnkucHk=..f7c06a8948e0cd1553ddada31ecf37c4776a6014_aGdpdGFseS9zZXJ2aWNlL3JlcG9zaXRvcnkucHk= 100644
--- a/hgitaly/service/repository.py
+++ b/hgitaly/service/repository.py
@@ -13,7 +13,6 @@
     archival,
     scmutil,
     hg,
-    vfs as vfsmod,
 )
 
 from heptapod.gitlab.branch import gitlab_branch_from_ref
@@ -25,6 +24,7 @@
 )
 
 from ..errors import (
+    already_exists,
     internal_error,
     invalid_argument,
     not_found,
@@ -276,4 +276,8 @@
             )
             return invalid_argument(context, CreateRepositoryResponse,
                                     message=msg)
+        if os.path.lexists(repo_path):
+            return already_exists(context, CreateRepositoryResponse,
+                                  message="A file or directory "
+                                  "exists already at %r" % repo_path)
         try:
@@ -279,12 +283,4 @@
         try:
-            # check if already an Hg repository (logic similar to upstream Hg)
-            wdir_vfs = vfsmod.vfs(repo_path, expandpath=True, realpath=True)
-            hgvfs = vfsmod.vfs(wdir_vfs.join(b'.hg'))
-            if hgvfs.exists():
-                # To match with Gitaly/Git behavior, if repository already
-                # initialized we should not return any error
-                # (although `hg init` would throw an error)
-                return CreateRepositoryResponse()
             hg.peer(self.ui, opts={}, path=repo_path, create=True)
         except Exception as exc:
             msg = (
diff --git a/hgitaly/service/tests/test_repository_service.py b/hgitaly/service/tests/test_repository_service.py
index 46369243c1c57f312002498eea962bbf26ba9ead_aGdpdGFseS9zZXJ2aWNlL3Rlc3RzL3Rlc3RfcmVwb3NpdG9yeV9zZXJ2aWNlLnB5..f7c06a8948e0cd1553ddada31ecf37c4776a6014_aGdpdGFseS9zZXJ2aWNlL3Rlc3RzL3Rlc3RfcmVwb3NpdG9yeV9zZXJ2aWNlLnB5 100644
--- a/hgitaly/service/tests/test_repository_service.py
+++ b/hgitaly/service/tests/test_repository_service.py
@@ -334,9 +334,12 @@
     wrapper = LocalRepoWrapper(hg.repository(ui, pathb), path)
     assert wrapper.repo.path.endswith(b'default/sample_repo/.hg')
 
-    # To follow along with Gitaly, attempt to create existing repo
-    # shouldn't respond with any error
-    do_rpc(rel_path)
+    # As of Gitaly 14.6, attempt to create existing repo is an error.
+
+    with pytest.raises(grpc.RpcError) as exc_info:
+        do_rpc(rel_path)
+    assert exc_info.value.code() == grpc.StatusCode.ALREADY_EXISTS
+    assert 'exists already' in exc_info.value.details()
 
     # when storage name doesn't exists
     with pytest.raises(grpc.RpcError) as exc_info:
@@ -345,8 +348,10 @@
     assert 'no such storage' in exc_info.value.details()
 
     # test with a broken symlink (which points to itself)
+    # This used to be a way to test failure in `hg init`, now should be
+    # refused as already existing path (see Gitaly Comparison tests)
     brepo_name = "myrepo_a_broken_symlink"
     path = (server_repos_root / default_storage / brepo_name)
     path.symlink_to(path)
     with pytest.raises(grpc.RpcError) as exc_info:
         do_rpc(brepo_name)
@@ -348,10 +353,24 @@
     brepo_name = "myrepo_a_broken_symlink"
     path = (server_repos_root / default_storage / brepo_name)
     path.symlink_to(path)
     with pytest.raises(grpc.RpcError) as exc_info:
         do_rpc(brepo_name)
-    assert exc_info.value.code() == grpc.StatusCode.INTERNAL
-    assert 'Too many levels of symbolic links' in exc_info.value.details()
+    assert exc_info.value.code() == grpc.StatusCode.ALREADY_EXISTS
+    assert 'exists already' in exc_info.value.details()
+
+    # using a broken symlink for the whole storage directory
+    # to pass the check for existing repo and still fail in `hg init`
+    rel_path = 'creation_error'
+    storage_path = server_repos_root / default_storage
+    try:
+        shutil.rmtree(storage_path)
+        storage_path.symlink_to(storage_path)
+        with pytest.raises(grpc.RpcError) as exc_info:
+            do_rpc(rel_path)
+        assert exc_info.value.code() == grpc.StatusCode.INTERNAL
+    finally:
+        storage_path.unlink()
+        storage_path.mkdir(exist_ok=True)
 
 
 def test_set_full_path(grpc_channel, server_repos_root):
diff --git a/tests_with_gitaly/test_repository_service.py b/tests_with_gitaly/test_repository_service.py
index 46369243c1c57f312002498eea962bbf26ba9ead_dGVzdHNfd2l0aF9naXRhbHkvdGVzdF9yZXBvc2l0b3J5X3NlcnZpY2UucHk=..f7c06a8948e0cd1553ddada31ecf37c4776a6014_dGVzdHNfd2l0aF9naXRhbHkvdGVzdF9yZXBvc2l0b3J5X3NlcnZpY2UucHk= 100644
--- a/tests_with_gitaly/test_repository_service.py
+++ b/tests_with_gitaly/test_repository_service.py
@@ -116,4 +116,6 @@
         response = repo_stubs[vcs].CreateRepository(request)
         return response
 
+    hg_rel_path = rel_path + '.hg'
+    git_rel_path = rel_path + '.git'
     # actual test
@@ -119,5 +121,12 @@
     # actual test
-    assert do_rpc('hg', rel_path) == do_rpc('git', rel_path)
+    assert do_rpc('hg', hg_rel_path) == do_rpc('git', git_rel_path)
+
+    # when repo already exists (actually its directory)
+    with pytest.raises(grpc.RpcError) as exc_info_git:
+        do_rpc('git', git_rel_path)
+    with pytest.raises(grpc.RpcError) as exc_info_hg:
+        do_rpc('hg', hg_rel_path)
+    assert exc_info_hg.value.code() == exc_info_git.value.code()
 
     # when storage name is invalid
     with pytest.raises(grpc.RpcError) as exc_info_hg:
@@ -129,6 +138,12 @@
     assert 'no such storage' in exc_info_git.value.details()
 
     # test with a broken symlink (which points to itself)
+    # it used to be an error for both, with Gitaly complaining
+    # that the file exists (but would not raise an error for
+    # an existing proper Git repo). As of 14.6, Gitaly refuses
+    # all existing files, including broken symlinks
+    # As a consequence of the added early check, we don't have
+    # any case of `hg init` itself failing on hand.
     repo_name = "myrepo_a_broken_symlink"
     path = (server_repos_root / default_storage / repo_name)
     path.symlink_to(path)
@@ -136,9 +151,10 @@
         do_rpc('hg', repo_name)
     with pytest.raises(grpc.RpcError) as exc_info_git:
         do_rpc('git', repo_name)
-    assert exc_info_hg.value.code() == exc_info_git.value.code()
-    assert 'Too many levels of symbolic links' in exc_info_hg.value.details()
-    assert 'file exists' in exc_info_git.value.details()
+    exc_hg, exc_git = exc_info_hg.value, exc_info_git.value
+    assert exc_hg.code() == exc_git.code()
+    for exc in (exc_hg, exc_git):
+        assert 'exists already' in exc.details()
 
 
 def test_set_full_path(gitaly_comparison, server_repos_root):