{"record":{"id":"6917c4af367e8ce0","repo":"astral-sh/ruff","slug":"tried-to-close-unavailable-document-key","errorCode":null,"errorMessage":"Tried to close unavailable document `{key}`","messagePattern":"Tried to close unavailable document `(.+?)`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_server/src/session/index.rs","lineNumber":327,"sourceCode":"        }\n        self.documents\n            .insert(notebook_uri, DocumentController::new_notebook(document));\n    }\n\n    pub(super) fn close_document(&mut self, key: &DocumentKey) -> crate::Result<()> {\n        // Notebook cells URIs are removed from the index here, instead of during\n        // `update_notebook_document`. This is because a notebook cell, as a text document,\n        // is requested to be `closed` by VS Code after the notebook gets updated.\n        // This is not documented in the LSP specification explicitly, and this assumption\n        // may need revisiting in the future as we support more editors with notebook support.\n        if let DocumentKey::NotebookCell(uri) = key {\n            if self.notebook_cells.remove(uri).is_none() {\n                tracing::warn!(\"Tried to remove a notebook cell that does not exist: {uri}\",);\n            }\n            return Ok(());\n        }\n        let Some(uri) = self.uri_for_key(key).cloned() else {\n            anyhow::bail!(\"Tried to close unavailable document `{key}`\");\n        };\n\n        let Some(_) = self.documents.remove(&uri) else {\n            anyhow::bail!(\"tried to close document that didn't exist at {uri}\")\n        };\n        Ok(())\n    }\n\n    pub(super) fn client_settings(&self, key: &DocumentKey) -> Option<Arc<ClientSettings>> {\n        let uri = self.uri_for_key(key)?;\n        let WorkspaceSettings {\n            client_settings, ..\n        } = self.settings_for_uri(uri)?;\n        Some(client_settings.clone())\n    }\n\n    fn document_controller_for_key(\n        &mut self,","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_server/src/session/index.rs#L309-L345","documentation":"`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.","triggerScenarios":"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`).","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// client-side: only send didClose for documents currently open\nif (openDocuments.has(uri)) {\n  openDocuments.delete(uri);\n  sendTextDocumentDidClose(uri);\n} // else skip duplicate close","typeGuard":null,"tryCatchPattern":"if let Err(e) = session.close_document(&key, enc) { if e.to_string().contains(\"Tried to close unavailable document\") { /* treat as already closed; continue */ } }","preventionTips":["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"],"tags":["lsp","documents","lifecycle"],"backgroundTag":"document-not-open","analyzedSha":"26f38c119cac42e4d320ba08f09224fdec74af2c","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}