{"record":{"id":"29f9e4e8a1f31a61","repo":"astral-sh/ruff","slug":"internalerror-29f9e4","errorCode":"InternalError","errorMessage":"Attempted to add edits for a document that was already edited","messagePattern":"Attempted to add edits for a document that was already edited","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_server/src/edit.rs","lineNumber":146,"sourceCode":"        Ok(())\n    }\n\n    /// Sets the edits made to a specific document. This should only be called\n    /// once for each document `uri`, and will fail if this is called for the same `uri`\n    /// multiple times.\n    pub(crate) fn set_edits_for_document(\n        &mut self,\n        uri: Uri,\n        _version: DocumentVersion,\n        edits: Vec<lsp_types::TextEdit>,\n    ) -> crate::Result<()> {\n        match self {\n            Self::DocumentChanges(document_edits) => {\n                if document_edits\n                    .iter()\n                    .any(|document| document.text_document.text_document_identifier.uri == uri)\n                {\n                    return Err(anyhow::anyhow!(\n                        \"Attempted to add edits for a document that was already edited\"\n                    ));\n                }\n                document_edits.push(lsp_types::TextDocumentEdit {\n                    text_document: lsp_types::OptionalVersionedTextDocumentIdentifier {\n                        text_document_identifier: TextDocumentIdentifier { uri },\n                        // TODO(jane): Re-enable versioned edits after investigating whether it could work with notebook cells\n                        version: None,\n                    },\n                    edits: edits.into_iter().map(lsp_types::Edit::TextEdit).collect(),\n                });\n                Ok(())\n            }\n            Self::Changes(changes) => {\n                if changes.get(&uri).is_some() {\n                    return Err(anyhow::anyhow!(\n                        \"Attempted to add edits for a document that was already edited\"\n                    ));","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/astral-sh/ruff/blob/d1087a4b9e03d253a88703f34e0869ee4b805456/crates/ruff_server/src/edit.rs#L128-L164","documentation":"While assembling a WorkspaceEdit, the server accumulates per-document edit sets and guards an invariant: at most one edit set per document URI. If set_edits_for_document is called twice for the same URI in the OptionalVersionedTextDocumentIdentifier-based documentChanges mode, it refuses with this InternalError instead of silently overwriting the earlier edits.","triggerScenarios":"A code path that applies two fix sources (e.g., fix-all plus organize-imports in one command, or a fix touching a notebook and its cell) both producing edits for the same file; duplicate resolve calls for the same document within one edit; regressions in the server's edit aggregation.","commonSituations":"Bugs in specific Ruff server versions combining commands on the same document; notebook workflows where both notebook-level and cell-level edits target one URI; races between concurrent fix requests on one file.","solutions":["Restart the language server / reload the window to clear in-flight edit state","Update to the latest Ruff server — duplicate-edit aggregation bugs get patched","Trigger the operations separately (autofix, then organize imports) instead of combined until fixed","If reproducible, capture tracing logs and file an issue with the command sequence"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Server-side (Rust): merge edits per URI instead of adding a second set\nuse std::collections::BTreeMap;\nlet mut per_uri: BTreeMap<Uri, Vec<lsp_types::TextEdit>> = BTreeMap::new();\nfor (uri, edits) in incoming {\n    per_uri.entry(uri).or_default().extend(edits);\n}\nfor (uri, edits) in per_uri {\n    edit_tracker.set_edits_for_document(uri, version, edits)?; // called once per URI\n}","typeGuard":null,"tryCatchPattern":"// Rust: a duplicate-edit failure is recoverable — log, keep the first edit set, continue\nif let Err(e) = tracker.set_edits_for_document(uri, version, edits) {\n    tracing::warn!(%e, %uri, \"skipping duplicate edit set\");\n}","preventionTips":["Aggregate all edits into one map keyed by URI before building the WorkspaceEdit","Apply combined operations sequentially (fix, then organize) rather than in one command","Report reproducible duplicate-edit sequences upstream with tracing logs"],"tags":["lsp","workspace-edit","internal-invariant","ruff-server"],"backgroundTag":"duplicate-workspace-edit","analyzedSha":"d1087a4b9e03d253a88703f34e0869ee4b805456","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}