NousResearch/hermes-agent · error · LSPProtocolError
cannot read {abs_path}: {e}
Error message
cannot read {abs_path}: {e} What it means
LSPProtocolError raised by open_file when reading the local file to send in didOpen/didChange fails with an OSError. The client must send the document text to the server, so an unreadable or missing path aborts the sync. Note errors='replace' is already set, so encoding problems do NOT trigger this — only real I/O failures.
Source
Thrown at agent/lsp/client.py:736
# ------------------------------------------------------------------
# public file-sync API
# ------------------------------------------------------------------
async def open_file(self, path: str, *, language_id: str = "plaintext") -> int:
"""Send didOpen (first time) or didChange (subsequent) for ``path``.
Returns the new document version number that the agent's
``wait_for_diagnostics`` should match against.
"""
if not self.is_running:
raise LSPProtocolError("client not running")
abs_path = os.path.abspath(path)
try:
text = Path(abs_path).read_text(encoding="utf-8", errors="replace")
except OSError as e:
raise LSPProtocolError(f"cannot read {abs_path}: {e}") from e
uri = file_uri(abs_path)
doc = self._docs.get(abs_path)
if doc is not None and doc.version >= 0:
# Re-open: bump version, fire didChangeWatchedFiles + didChange.
await self._send_notification(
"workspace/didChangeWatchedFiles",
{"changes": [{"uri": uri, "type": 2}]}, # 2 = CHANGED
)
new_version = doc.version + 1
old_text = doc.text
content_changes: List[Dict[str, Any]]
if self._sync_kind == 2:
content_changes = [
{
"range": {
"start": {"line": 0, "character": 0},View on GitHub (pinned to c896c09c42)
Solutions
- Validate the file exists and is readable before syncing (Path.is_file() plus an os.access check).
- If the file was renamed/deleted intentionally, close it on the server side (didClose) instead of opening it.
- Fix permissions or run the agent with read access to the workspace root.
Example fix
# before
await client.open_file(deleted_path) # LSPProtocolError: cannot read
# after
from pathlib import Path
p = Path(path)
if not p.is_file():
await client.close_file(path) # sync deletion with the server
else:
await client.open_file(path) Defensive patterns
Strategy: validation
Validate before calling
import os
from pathlib import Path
def file_readable(path: str) -> bool:
p = Path(path)
return p.is_file() and os.access(p, os.R_OK) Try / catch
try:
version = await client.open_file(path)
except LSPProtocolError as e:
if "cannot read" in str(e):
skip_or_close_document(path) # deleted/unreadable: sync deletion instead
else:
raise Prevention
- Check Path.is_file() before syncing; editors racing deletions are the usual cause.
- Sync deletions with didClose instead of trying to open vanished files.
- Run the agent with read access to the whole workspace root.
When it happens
Trigger: open_file on a path that was deleted between listing and sync; a directory passed instead of a file; permission denied (mode 000, foreign ownership); path too long or otherwise invalid for the OS.
Common situations: Editor/agent racing file deletion or rename (git checkout switching branches); paths from external systems that no longer exist; sandboxed environments without read access to the workspace.
Related errors
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/4308f8592994d0df.
Report an issue: GitHub.