astral-sh/ruff · error
Tried to close unavailable document
Error message
Tried to close unavailable document `{key}` What it means
`close_document` removes a document from the session index on `textDocument/didClose`. If the key has no indexed URI, the server cannot locate the document to remove and bails with this message. Notebook cells are handled earlier and excluded from this path.
Solutions
- Ensure each didClose corresponds to a previously sent didOpen for the same URI
- De-duplicate close notifications in the client before sending them
- Restart the Ruff server to resynchronize session state, then reopen documents as needed
Defensive patterns
Strategy: validation
Validate before calling
// client-side: only send didClose for documents currently open
if (openDocuments.has(uri)) {
openDocuments.delete(uri);
sendTextDocumentDidClose(uri);
} // else skip duplicate close Try / catch
if let Err(e) = session.close_document(&key, enc) { if e.to_string().contains("Tried to close unavailable document") { /* treat as already closed; continue */ } } Prevention
- De-duplicate textDocument/didClose notifications in the client
- Send didClose exactly once per didOpen, matching the same URI
- After a server restart, reopen documents before further interactions
When it happens
Trigger: Calling `session.close_document(key, encoding)` where `uri_for_key(key)` returns None — closing a document that was never opened, was already closed, or belongs to a notebook cell (cells take the early-return path only when found in `notebook_cells`).
Common situations: Duplicate `textDocument/didClose` notifications from an editor plugin; client sends didClose after a server restart lost session state; closing a notebook cell URI that was never registered.
Related errors
- client exited without proper shutdown sequence
- Document controller not available at
- Notebook document URI does not point to a notebook document
- Received exit notification before a shutdown request
- Received exit notification before a shutdown request
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/6917c4af367e8ce0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/ruff_server/src/session/index.rs:327
}
self.documents
.insert(notebook_uri, DocumentController::new_notebook(document));
}
pub(super) fn close_document(&mut self, key: &DocumentKey) -> crate::Result<()> {
// Notebook cells URIs are removed from the index here, instead of during
// `update_notebook_document`. This is because a notebook cell, as a text document,
// is requested to be `closed` by VS Code after the notebook gets updated.
// This is not documented in the LSP specification explicitly, and this assumption
// may need revisiting in the future as we support more editors with notebook support.
if let DocumentKey::NotebookCell(uri) = key {
if self.notebook_cells.remove(uri).is_none() {
tracing::warn!("Tried to remove a notebook cell that does not exist: {uri}",);
}
return Ok(());
}
let Some(uri) = self.uri_for_key(key).cloned() else {
anyhow::bail!("Tried to close unavailable document `{key}`");
};
let Some(_) = self.documents.remove(&uri) else {
anyhow::bail!("tried to close document that didn't exist at {uri}")
};
Ok(())
}
pub(super) fn client_settings(&self, key: &DocumentKey) -> Option<Arc<ClientSettings>> {
let uri = self.uri_for_key(key)?;
let WorkspaceSettings {
client_settings, ..
} = self.settings_for_uri(uri)?;
Some(client_settings.clone())
}
fn document_controller_for_key(
&mut self,View on GitHub (pinned to 26f38c119c)