Kuberwastaken/claurst · error · anyhow::Error

Cannot read ' ' for LSP

Error message

Cannot read '{}' for LSP: {}

What it means

Thrown in `LspManager::open_file` when `tokio::fs::read_to_string(file_path)` fails while loading file content to send as a `textDocument/didOpen` notification. The raw IO error (NotFound, PermissionDenied, invalid UTF-8, etc.) is embedded in the message. The file must be readable as UTF-8 text to be opened on the LSP server.

Solutions

  1. Verify the path exists and is a regular file before calling open_file (std::path::Path::is_file).
  2. Check file permissions and that the process user can read it.
  3. Ensure the file is valid UTF-8; skip or convert non-UTF-8 files before LSP open.
  4. Refresh any cached/stale path against the current filesystem.

Example fix

// before: unconditional open
manager.open_file(path, root).await?;
// after: pre-check the file
let p = std::path::Path::new(path);
if p.is_file() {
    manager.open_file(path, root).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

let p = std::path::Path::new(path);
if !p.is_file() { bail!("not a readable file: {path}"); }
// optional UTF-8 pre-check
std::fs::read(p).and_then(|b| String::from_utf8(b).map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "not UTF-8")))?;

Type guard

fn openable(path: &str) -> bool {
    std::path::Path::new(path).is_file()
}

Try / catch

match manager.open_file(path, root).await {
    Ok(()) => {},
    Err(e) if e.to_string().starts_with("Cannot read") => {
        tracing::warn!("skipping LSP open for unreadable file {path}: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: open_file(file_path, root_dir) is called with a path that does not exist, was deleted, lacks read permission, is a directory, or contains invalid UTF-8 bytes.

Common situations: Stale buffer referencing a deleted file; opening binary or non-UTF-8 files; typo in path or wrong working directory; file locked by permissions (e.g. root-owned file, sandboxed environment).

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/41f05c6f6143b417. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/core/src/lsp.rs:1068

        &mut self,
        file_path: &str,
        root_dir: &Path,
    ) -> anyhow::Result<()> {
        let uri = path_to_uri(file_path);
        let server_name = match self.server_name_for_file(file_path) {
            Some(n) => n.to_string(),
            None => return Ok(()),
        };

        // Skip if already opened on this server
        if self.opened_files.get(&uri).map(|s| s.as_str()) == Some(server_name.as_str()) {
            return Ok(());
        }

        let content = match tokio::fs::read_to_string(file_path).await {
            Ok(c) => c,
            Err(e) => {
                return Err(anyhow::anyhow!(
                    "Cannot read '{}' for LSP: {}",
                    file_path,
                    e
                ))
            }
        };

        // Ensure the server is running first (borrows self mutably, so must
        // finish before we borrow opened_files).
        self.ensure_started(file_path, root_dir).await?;

        if let Some(client) = self.clients.get_mut(&server_name) {
            let lang = client.server_config.language_for_file(file_path);
            client.open_document(&uri, &lang, &content).await?;
            self.opened_files.insert(uri, server_name);
        }
        Ok(())
    }

View on GitHub (pinned to b0637c97ec)