{"record":{"id":"1863cb449d9d0590","repo":"astral-sh/ruff","slug":"notebook-document-path-does-not-point-to-a-noteboo","errorCode":null,"errorMessage":"Notebook document path does not point to a notebook document","messagePattern":"Notebook document path does not point to a notebook document","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_server/src/session/index.rs","lineNumber":76,"sourceCode":"    #[expect(dead_code)]\n    fn notebook_document_keys(&self) -> impl Iterator<Item = &DocumentKey> + '_ {\n        self.documents\n            .iter()\n            .filter(|(_, doc)| doc.as_notebook().is_some())\n            .map(|(key, _)| key)\n    }\n\n    pub(super) fn update_notebook_document(\n        &mut self,\n        notebook_key: &DocumentKey,\n        cells: Option<lsp_types::NotebookDocumentCellChanges>,\n        metadata: Option<serde_json::Map<String, serde_json::Value>>,\n        new_version: DocumentVersion,\n        encoding: PositionEncoding,\n    ) -> crate::Result<()> {\n        let document = self.document_mut(notebook_key)?;\n        let Some(notebook) = document.as_notebook_mut() else {\n            anyhow::bail!(\"Notebook document path does not point to a notebook document\");\n        };\n\n        let (structure, data, text_content) = cells\n            .map(|cells| {\n                let lsp_types::NotebookDocumentCellChanges {\n                    structure,\n                    data,\n                    text_content,\n                } = cells;\n                (structure, data, text_content)\n            })\n            .unwrap_or_default();\n\n        let (array, did_open, did_close) = structure\n            .map(|structure| {\n                let lsp_types::NotebookDocumentCellChangeStructure {\n                    array,\n                    did_open,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/astral-sh/ruff/blob/672bb4edf04c84f8b0753346359daee4057158f2/crates/ty_server/src/session/index.rs#L58-L94","documentation":"On notebookDocument/didChange, the notebook key resolved to a document that is not a notebook (as_notebook_mut() returned None), so update_notebook_document bails (index.rs:76). The index stores text and notebook documents under distinct key namespaces; a mismatch means the client used the notebook API for a plain text document.","triggerScenarios":"Sending notebookDocument/didChange for a URI the server stored as a text document — e.g. never sending didOpenNotebookDocument first, or addressing a regular .py file with the notebook notification.","commonSituations":"Clients skipping notebook didOpen, restart races where the editor replays didChange for a notebook the server never saw, or a mismatched pair of client/server notebook-protocol versions.","solutions":["Always open via didOpenNotebookDocument before sending notebook didChange","Match the exact notebookDocument.uri used in the didOpen","After a desync, reopen the notebook file to re-register it as a notebook"],"exampleFix":"// before\nnotify('notebookDocument/didChange', { notebookDocument: { uri, version }, ... });\n\n// after\nnotify('notebookDocument/didOpen', { notebookDocument: { uri, version, notebookType, cells } });\nnotify('notebookDocument/didChange', { notebookDocument: { uri, version }, change });","handlingStrategy":"type-guard","validationCode":"// TS: only send notebook didChange for URIs opened as notebooks\nconst openNotebooks = new Set<string>();\nclient.onNotification('notebookDocument/didOpen', () => {}); // track via your own didOpen calls\nfunction changeNotebook(uri: string, change: unknown) {\n  if (!openNotebooks.has(uri)) return; // never opened -> would hit the bail\n  notify('notebookDocument/didChange', { notebookDocument: { uri, version: next() }, change });\n}","typeGuard":"const isOpenNotebook = (uri: string): boolean => openNotebooks.has(uri);","tryCatchPattern":null,"preventionTips":["Pair every notebookDocument/didChange with a prior didOpenNotebookDocument of the same URI","On editor restart/replay, re-send notebook didOpen before didChange","Don't send notebook notifications for plain .py files — the server stores those as text documents"],"tags":["lsp","notebook","did-change","document-mismatch"],"backgroundTag":null,"analyzedSha":"672bb4edf04c84f8b0753346359daee4057158f2","analyzedAt":"2026-08-16T08:54:05.464Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}