{"record":{"id":"279ef325b36ced5d","repo":"astral-sh/ruff","slug":"text-document-uri-does-not-point-to-a-text-documen","errorCode":null,"errorMessage":"Text document URI does not point to a text document","messagePattern":"Text document URI does not point to a text document","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_server/src/session/index.rs","lineNumber":114,"sourceCode":"    }\n\n    pub(super) fn notebook_document_uris(&self) -> impl Iterator<Item = &Uri> + '_ {\n        self.documents\n            .iter()\n            .filter(|(_, doc)| doc.as_notebook().is_some())\n            .map(|(uri, _)| uri)\n    }\n\n    pub(super) fn update_text_document(\n        &mut self,\n        key: &DocumentKey,\n        content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,\n        new_version: DocumentVersion,\n        encoding: PositionEncoding,\n    ) -> crate::Result<()> {\n        let controller = self.document_controller_for_key(key)?;\n        let Some(document) = controller.as_text_mut() else {\n            anyhow::bail!(\"Text document URI does not point to a text document\");\n        };\n\n        if content_changes.is_empty() {\n            document.update_version(new_version);\n            return Ok(());\n        }\n\n        document.apply_changes(content_changes, new_version, encoding);\n\n        Ok(())\n    }\n\n    pub(super) fn key_from_uri(&self, uri: Uri) -> DocumentKey {\n        if self.notebook_cells.contains_key(&uri) {\n            DocumentKey::NotebookCell(uri)\n        } else if self\n            .documents\n            .get(&uri)","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_server/src/session/index.rs#L96-L132","documentation":"`update_text_document` applies `textDocument/didChange` content edits to an indexed document. If the controller stored under the given document key is not a text document (e.g. it is a notebook), the update cannot proceed and the server bails. This guards against applying plain-text incremental edits to non-text document types.","triggerScenarios":"Calling `session.update_text_document(key, content_changes, new_version, encoding)` where `document_controller_for_key` returns a `DocumentController` whose inner document is a notebook rather than text; `as_text_mut()` returns None.","commonSituations":"A client sends `textDocument/didChange` for a URI that Ruff indexed as part of a notebook (`notebookDocument/didOpen`); mixed-language editors sending text updates for cell documents.","solutions":["Send `notebookDocument/didChange` for notebook documents instead of `textDocument/didChange`","Verify the URI in didChange matches the URI that was opened via `textDocument/didOpen`","Close and reopen the document if its type changed server-side (restart the Ruff server to reset state)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// client-side check before sending didChange\nif (openedAsNotebook.has(uri)) {\n  sendNotebookDidChange(doc); // not textDocument/didChange\n} else {\n  sendTextDocumentDidChange(doc);\n}","typeGuard":"fn is_text_document(controller: &DocumentController) -> bool { matches!(controller.as_text(), Some(_)) }","tryCatchPattern":"if let Err(e) = session.update_text_document(&key, changes, version, enc) { if e.to_string().contains(\"not point to a text document\") { /* resend via notebook path */ } }","preventionTips":["Track document kinds client-side and route change notifications to the matching LSP method","Never send textDocument/didChange for URIs opened via notebookDocument/didOpen","Reopen documents with the correct lifecycle when their type appears to change"],"tags":["lsp","documents","notebook"],"backgroundTag":"wrong-document-type","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"}