{"record":{"id":"d8530cddabb2240d","repo":"NousResearch/hermes-agent","slug":"client-not-running","errorCode":null,"errorMessage":"client not running","messagePattern":"client not running","errorType":"exception","errorClass":"LSPProtocolError","httpStatus":null,"severity":"error","filePath":"agent/lsp/client.py","lineNumber":730,"sourceCode":"        # keep the Event sticky-set so any wait already in progress\n        # resolves; waiters re-check their predicate after waking and\n        # decide whether to keep waiting.  ``_push_counter`` is what\n        # they actually compare against to detect a fresh event.\n        self._push_counter += 1\n        self._push_event.set()\n\n    # ------------------------------------------------------------------\n    # public file-sync API\n    # ------------------------------------------------------------------\n\n    async def open_file(self, path: str, *, language_id: str = \"plaintext\") -> int:\n        \"\"\"Send didOpen (first time) or didChange (subsequent) for ``path``.\n\n        Returns the new document version number that the agent's\n        ``wait_for_diagnostics`` should match against.\n        \"\"\"\n        if not self.is_running:\n            raise LSPProtocolError(\"client not running\")\n\n        abs_path = os.path.abspath(path)\n        try:\n            text = Path(abs_path).read_text(encoding=\"utf-8\", errors=\"replace\")\n        except OSError as e:\n            raise LSPProtocolError(f\"cannot read {abs_path}: {e}\") from e\n\n        uri = file_uri(abs_path)\n        doc = self._docs.get(abs_path)\n\n        if doc is not None and doc.version >= 0:\n            # Re-open: bump version, fire didChangeWatchedFiles + didChange.\n            await self._send_notification(\n                \"workspace/didChangeWatchedFiles\",\n                {\"changes\": [{\"uri\": uri, \"type\": 2}]},  # 2 = CHANGED\n            )\n            new_version = doc.version + 1\n            old_text = doc.text","sourceCodeStart":712,"sourceCodeEnd":748,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/lsp/client.py#L712-L748","documentation":"LSPProtocolError raised by open_file when client.is_running is false — the caller asked to sync a file with a server that is not alive. It is a lifecycle misuse error: open_file must be called between start() and stop()/server exit.","triggerScenarios":"Calling open_file before start() completed, after stop(), or after the server process exited on its own; also when start() failed earlier (e.g. binary not found) and the failure was swallowed.","commonSituations":"Fire-and-forget startup where callers do not await start(); a crashed server leaving a half-alive client object; test fixtures reusing a stopped client.","solutions":["Await start() before any file sync, and guard every open_file with an is_running check.","If the server crashed, restart the client (fresh process, re-open all relevant documents) rather than reusing the dead one.","Propagate startup errors instead of ignoring them, so a not-running client is never silently used."],"exampleFix":"# before\nawait client.open_file(\"src/main.py\")  # may raise: client not running\n\n# after\nif not client.is_running:\n    await client.start()\nawait client.open_file(\"src/main.py\")","handlingStrategy":"type-guard","validationCode":"if not client.is_running:\n    await client.start()\nversion = await client.open_file(path)","typeGuard":"async def ensure_running(client) -> bool:\n    \"\"\"True if the client is alive; starts it if not yet started.\"\"\"\n    if client.is_running:\n        return True\n    try:\n        await client.start()\n        return client.is_running\n    except LSPProtocolError:\n        return False","tryCatchPattern":null,"preventionTips":["Always await start() and check is_running before file-sync calls.","Never swallow startup exceptions — a half-started client guarantees this error later.","Guard all file-sync entry points (open_file/close_file) with an is_running check."],"tags":["lsp","lifecycle","api-misuse"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}