diff --git a/docs/changes/4.4.3.md b/docs/changes/4.4.3.md
index 09ca45272..208cb6a68 100644
--- a/docs/changes/4.4.3.md
+++ b/docs/changes/4.4.3.md
@@ -13,6 +13,7 @@ Release date: `2020-xx-xx`
 - [NXDRIVE-2113](https://jira.nuxeo.com/browse/NXDRIVE-2113): Add a warning on upload when server-side lock is disabled
 - [NXDRIVE-2116](https://jira.nuxeo.com/browse/NXDRIVE-2116): Requests `Invalid byte range` multiple of total binary size
 - [NXDRIVE-2124](https://jira.nuxeo.com/browse/NXDRIVE-2124): Uniformize the name: "Direct Edit"
+- [NXDRIVE-2128](https://jira.nuxeo.com/browse/NXDRIVE-2128): Allow only one Direct Edit'ion per document
 
 ## GUI
 
diff --git a/nxdrive/direct_edit.py b/nxdrive/direct_edit.py
index b76568c1f..958808970 100644
--- a/nxdrive/direct_edit.py
+++ b/nxdrive/direct_edit.py
@@ -85,6 +85,7 @@ def __init__(self, manager: "Manager", folder: Path) -> None:
         self.use_autolock = self._manager.get_direct_edit_auto_lock()
         self._event_handler: Optional[DriveFSEventHandler] = None
         self._metrics = {"edit_files": 0}
+        self._opened_files = set()
         self._observer: Observer = None
         self.local = LocalClient(self._folder)
         self._upload_queue: Queue = Queue()
@@ -531,7 +532,10 @@ def edit(
             self.directEditError.emit("DIRECT_EDIT_NOT_ENABLED", [])
             return
 
+        if doc_id in self._opened_files:
+            return
         log.info(f"Direct Editing doc {doc_id!r} on {server_url!r}")
+        self._opened_files.add(doc_id)
         try:
             # Download the file
             file_path = self._prepare_edit(
@@ -542,12 +546,17 @@ def edit(
             # Launch it
             if file_path:
                 self._manager.open_local_file(file_path)
+            else:
+                self._opened_files.discard(doc_id)
         except OSError as e:
             if e.errno == errno.EACCES:
                 # Open file anyway
                 if e.filename is not None:
                     self._manager.open_local_file(e.filename)
+                else:
+                    self._opened_files.discard(doc_id)
             else:
+                self._opened_files.discard(doc_id)
                 raise e
 
     def _extract_edit_info(self, ref: Path) -> DirectEditDetails:
@@ -657,6 +666,7 @@ def _handle_lock_queue(self) -> None:
                     continue
 
                 self.local.remove_remote_id(ref.parent, name="nxdirecteditlock")
+                self._opened_files.discard(ref)
                 # Emit the signal only when the unlock is done
                 self._send_lock_status(ref)
                 self.autolock.documentUnlocked.emit(ref.name)
diff --git a/tests/old_functional/test_direct_edit.py b/tests/old_functional/test_direct_edit.py
index 5efae42e5..dd4edf80d 100644
--- a/tests/old_functional/test_direct_edit.py
+++ b/tests/old_functional/test_direct_edit.py
@@ -1,4 +1,5 @@
 # coding: utf-8
+import errno
 import time
 from logging import getLogger
 from pathlib import Path
@@ -486,6 +487,106 @@ def request(*args, **kwargs):
             assert isinstance(file, Path)
             assert file.is_file()
 
+    def test_concurrent_editions_on_same_document(self):
+        """Simulate Direct Edit'ing a file more than one-time.
+        It happens often when a user double-clic on the edit button on the browser,
+        then 2 events are sent at almost the same time.
+        See NXDRIVE-2128 for details.
+        """
+        filename = "multiplication des clics.txt"
+        uid = self.remote.make_file("/", filename, content=b"Plein de clics.")
+        download_url = f"nxfile/default/{uid}/file:content/{filename}"
+        args = (self.nuxeo_url, uid)
+        kwargs = {"user": self.user_1, "download_url": download_url}
+        call_count = 0
+
+        def open_local_file(*args, **kwargs):
+            nonlocal call_count
+            call_count += 1
+
+        with patch.object(self.manager_1, "open_local_file", new=open_local_file):
+            self.direct_edit.edit(*args, **kwargs)
+            self.direct_edit.edit(*args, **kwargs)
+            self.direct_edit.edit(*args, **kwargs)
+            self.direct_edit.edit(*args, **kwargs)
+        assert len(self.direct_edit._opened_files) == 1
+        assert uid in self.direct_edit._opened_files
+        assert call_count == 1
+
+    def test_concurrent_editions_on_same_document_with_download_failures(self):
+        """Simulate Direct Edit'ing a file more than one-time.
+        It happens often when a user double-clic on the edit button on the browser,
+        then 2 events are sent at almost the same time.
+        In this scenario, the download will fail and the file is not opened.
+        See NXDRIVE-2128 for details.
+        """
+        filename = "multiplication des clics fails.txt"
+        uid = self.remote.make_file("/", filename, content=b"Plein de clics.")
+        download_url = f"nxfile/default/{uid}/file:content/{filename}"
+        args = (self.nuxeo_url, uid)
+        kwargs = {"user": self.user_1, "download_url": download_url}
+        call_count = 0
+
+        def open_local_file(*args, **kwargs):
+            nonlocal call_count
+            call_count += 1
+
+        def prepare_edit_none(*args, **kwargs):
+            return None
+
+        def prepare_edit_raise_os_error_no_filename(*args, **kwargs):
+            raise OSError(errno.EACCES, "Mock'ed OS Error")
+
+        def prepare_edit_raise_os_error_with_filename(*args, **kwargs):
+            raise OSError(errno.EACCES, "Mock'ed OS Error", "/dqqdqdqadqdadzdzdq/")
+
+        def prepare_edit_raise_random_os_error_with_filename(*args, **kwargs):
+            raise OSError(errno.EEXIST, "Mock'ed OS Error")
+
+        with patch.object(
+            self.manager_1, "open_local_file", new=open_local_file
+        ), patch.object(self.direct_edit, "_prepare_edit", new=prepare_edit_none):
+            self.direct_edit.edit(*args, **kwargs)
+            assert len(self.direct_edit._opened_files) == 0
+            assert call_count == 0
+
+        with patch.object(
+            self.manager_1, "open_local_file", new=open_local_file
+        ), patch.object(
+            self.direct_edit,
+            "_prepare_edit",
+            new=prepare_edit_raise_os_error_no_filename,
+        ):
+            self.direct_edit.edit(*args, **kwargs)
+            assert len(self.direct_edit._opened_files) == 0
+            assert call_count == 0
+
+        with patch.object(
+            self.manager_1, "open_local_file", new=open_local_file
+        ), patch.object(
+            self.direct_edit,
+            "_prepare_edit",
+            new=prepare_edit_raise_random_os_error_with_filename,
+        ):
+            with pytest.raises(OSError):
+                self.direct_edit.edit(*args, **kwargs)
+            assert len(self.direct_edit._opened_files) == 0
+            assert call_count == 0
+
+        with patch.object(
+            self.manager_1, "open_local_file", new=open_local_file
+        ), patch.object(
+            self.direct_edit,
+            "_prepare_edit",
+            new=prepare_edit_raise_os_error_with_filename,
+        ):
+            self.direct_edit.edit(*args, **kwargs)
+            self.direct_edit.edit(*args, **kwargs)
+            self.direct_edit.edit(*args, **kwargs)
+            self.direct_edit.edit(*args, **kwargs)
+            assert len(self.direct_edit._opened_files) == 1
+            assert call_count == 1
+
     def test_self_locked_file(self):
         filename = "Mode operatoire.txt"
         doc_id = self.remote.make_file("/", filename, content=b"Some content.")
