astral-sh/ruff · error

Tried to open unavailable document

Error message

Tried to open unavailable document `{key}`

What it means

When a `notebookDocument/didChange` includes a cell-structure change with `did_open` cells, the server first resolves the notebook's URI from the document key. If no URI is indexed for that key, it bails because it cannot register the newly opened cells under the notebook's path. This prevents orphaned cell mappings in `self.notebook_cells`.

Solutions

  1. Ensure `notebookDocument/didOpen` was sent before sending didChange with did_open cell events
  2. Reopen the notebook (didOpen) if it was closed, then resend the change
  3. Restart the Ruff server if its session state has desynchronized from the client
Defensive patterns

Strategy: validation

Validate before calling

// client-side: only send did_open structure changes for notebooks already opened via didOpen
if (!openedNotebooks.has(notebookUri)) {
  await sendNotebookDidOpen(notebook); // open first
}
sendNotebookDidChangeWithCellOpens(notebook);

Try / catch

if let Err(e) = session.update_notebook_document(&key, cells, meta, ver, enc) { if e.to_string().contains("Tried to open unavailable document") { /* reopen notebook then retry once */ } }

Prevention

When it happens

Trigger: Calling `session.update_notebook_document` with a `NotebookDocumentCellChangeStructure` whose `did_open` is Some, while `uri_for_key(key)` returns None — i.e. the notebook was never opened or was already closed.

Common situations: Client sends didChange with open-cell structure events for a notebook that was closed via `notebookDocument/didClose`; out-of-order LSP notifications after a server restart; notebook opened in one workspace but edited via a key from another.

Related errors


AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/04678c588d4d24fb. Report an issue: GitHub.

Appendix: source

Thrown at crates/ruff_server/src/session/index.rs:156

        }
    }

    pub(super) fn update_notebook_document(
        &mut self,
        key: &DocumentKey,
        cells: Option<lsp_types::NotebookDocumentCellChanges>,
        metadata: Option<serde_json::Map<String, serde_json::Value>>,
        new_version: DocumentVersion,
        encoding: PositionEncoding,
    ) -> crate::Result<()> {
        // update notebook cell index
        if let Some(lsp_types::NotebookDocumentCellChangeStructure {
            did_open: Some(did_open),
            ..
        }) = cells.as_ref().and_then(|cells| cells.structure.as_ref())
        {
            let Some(path) = self.uri_for_key(key).cloned() else {
                anyhow::bail!("Tried to open unavailable document `{key}`");
            };

            for opened_cell in did_open {
                self.notebook_cells
                    .insert(opened_cell.uri.clone(), path.clone());
            }
            // deleted notebook cells are closed via textDocument/didClose - we don't close them here.
        }

        let controller = self.document_controller_for_key(key)?;
        let Some(notebook) = controller.as_notebook_mut() else {
            anyhow::bail!("Notebook document URI does not point to a notebook document");
        };

        notebook.update(cells, metadata, new_version, encoding)?;
        Ok(())
    }

View on GitHub (pinned to 26f38c119c)