diff --git a/lib/esbonio/esbonio/server/features/sphinx_manager/manager.py b/lib/esbonio/esbonio/server/features/sphinx_manager/manager.py index c0246288..7cee14f1 100644 --- a/lib/esbonio/esbonio/server/features/sphinx_manager/manager.py +++ b/lib/esbonio/esbonio/server/features/sphinx_manager/manager.py @@ -238,8 +238,18 @@ async def get_client(self, uri: Uri) -> SphinxClient | None: partial(self._create_or_replace_client, uri), scope=uri, ) - # The first few callers in a given scope will miss out, but that shouldn't matter - # too much + + # It's possible for this code path to be hit multiple times in quick + # succession e.g. on a fresh server start with N .rst files already open, + # creating the opportunity to accidentally spawn N duplicated clients! + # + # To prevent this, store a `None` at this scope, all being well it will be + # replaced with the actual client instance when the + # `_create_or_replace_client` callback runs. + self.clients[scope] = None + + # The first few callers in a given scope will miss out, but that shouldn't + # matter too much return None if (client := self.clients[scope]) is None: