astral-sh/ruff · error · std::io::Error
NotFound
NotFound
Error message
Virtual path does not exist: {path} What it means
Virtual paths only exist while their document is open in the LSP index. read_virtual_path_to_string/read_virtual_path_to_notebook call system_virtual_path_to_document and, when no open document matches, return ErrorKind::NotFound with 'Virtual path does not exist: {path}'.
Source
Thrown at crates/ty_server/src/system.rs:347
))
}
fn dyn_clone(&self) -> Box<dyn CommandExecutor> {
Box::new(Self)
}
}
fn not_a_text_document(path: impl Display) -> std::io::Error {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Input is not a text document: {path}"),
)
}
fn virtual_path_not_found(path: impl Display) -> std::io::Error {
std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Virtual path does not exist: {path}"),
)
}
/// Helper function to get the [`FileRevision`] of the given document.
fn document_revision(document: &Document, index: &Index) -> FileRevision {
// The file revision is just an opaque number which doesn't have any significant meaning other
// than that the file has changed if the revisions are different.
#[expect(clippy::cast_sign_loss)]
match document {
Document::Text(text) => FileRevision::new(text.version() as u128),
Document::Notebook(notebook) => {
// VS Code doesn't always bump the notebook version when the cell content changes.
// Specifically, I noticed that VS Code re-uses the same version when:
// 1. Adding a new cell
// 2. Pasting some code that has an error
//
// The notification updating the cell content on paste re-used the same version as when the cell was added.
// Because of that, hash all cell versions and the notebook versions together.View on GitHub (pinned to d1087a4b9e)
Solutions
- Ensure textDocument/didOpen (or notebook document sync) completes before issuing requests that touch the document
- Cancel or discard work for documents that receive didClose
- Validate the virtual path against the open-document index before reading
Defensive patterns
Strategy: validation
Validate before calling
// Issue requests only while the document is open
if (!openDocuments.has(virtualPath)) {
await connection.sendRequest('textDocument/didOpen' /* ensure open first */);
} Try / catch
match system.read_virtual_path_to_string(path) {
Ok(text) => { /* use text */ }
Err(e) if e.kind() == std::io::ErrorKind::NotFound => { /* document closed: drop the stale work item */ }
Err(e) => return Err(e),
} Prevention
- Send didOpen before any feature request touching a document
- Cancel in-flight work on didClose
- Never cache virtual paths across server restarts
When it happens
Trigger: ty reads a virtual path (untitled file, notebook cell) after the client sent didClose, before the first didOpen, or for a document that was never synced by this client.
Common situations: In-flight requests racing a document close (hover finishing after close); editors that skip syncing untitled files; retries against a restarted server holding stale paths.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- InternalError
- InvalidInput
- Text document path does not point to a text document
- InternalError
- InternalError
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/a7b8f683242c7521.
Report an issue: GitHub.