{"record":{"id":"c6f7c83a933536a2","repo":"rust-lang/rust-analyzer","slug":"an-edit-target-must-still-be-present","errorCode":null,"errorMessage":"an edit target must still be present","messagePattern":"an edit target must still be present","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/syntax/src/syntax_editor/edit_algo.rs","lineNumber":418,"sourceCode":"    fn map_original_path(&self, mut path: SyntaxPath) -> Option<SyntaxPath> {\n        for edit in &self.edits {\n            match edit {\n                PathEdit::Splice { parent, deleted, inserted } => {\n                    if !path.adjust_for_splice(parent, deleted, *inserted) {\n                        return None;\n                    }\n                }\n                PathEdit::ReplaceRoot => return None,\n            }\n        }\n        Some(path)\n    }\n\n    /// Finds a change target in the current root.\n    fn map_original_element(&self, element: &SyntaxElement) -> SyntaxElement {\n        self.map_original_path(SyntaxPath::new(element))\n            .and_then(|path| path.resolve(&self.root))\n            .expect(\"an edit target must still be present\")\n    }\n\n    /// Applies one child-list splice and updates tracked structural path.\n    fn splice(\n        &mut self,\n        parent_path: SyntaxPath,\n        deleted: Range<usize>,\n        inserted: Vec<PreparedElement>,\n        track_as_changed: bool,\n    ) {\n        let inserted_count = inserted.len();\n        self.changed\n            .retain_mut(|path| path.adjust_for_splice(&parent_path, &deleted, inserted_count));\n        self.annotations\n            .retain_mut(|it| it.path.adjust_for_splice(&parent_path, &deleted, inserted_count));\n\n        for (offset, element) in inserted.iter().enumerate() {\n            let index = deleted.start + offset;","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/syntax/src/syntax_editor/edit_algo.rs#L400-L436","documentation":"`map_original_element` translates an element's structural path from the original tree through the recorded edits and resolves it against the current root. The `expect` asserts the library's bookkeeping guarantees that any element being targeted by an edit still exists in the current tree. If path adjustment (`adjust_for_splice` returning None, or `ReplaceRoot`) or resolution fails, internal edit tracking has a bug.","triggerScenarios":"Internal: replaying recorded splices deletes/moves the very element being looked up (tracking missed a splice), or the root was replaced (`PathEdit::ReplaceRoot`) making the original path unresolvable. As an API user this should be unreachable; it surfaces only if SyntaxEditor bookkeeping has a bug or edits are applied out of the API's contract.","commonSituations":"Practically seen only when hacking on crates/syntax syntax_editor internals, applying an element's edits after the root was fully replaced, or interleaving manual tree mutations with editor-tracked edits.","solutions":["Verify you only mutate the tree through the SyntaxEditor API (no direct replacements of the tracked root)","Ensure all edits for an element are applied in the documented order without replacing the root mid-batch","Reproduce with a minimal fixture and file/inspect a bug against rust-analyzer's syntax_editor if reachable from public API","When editing internals, make `map_original_element` return `Option<SyntaxElement>` and handle the missing target gracefully"],"exampleFix":"// before\n.and_then(|path| path.resolve(&self.root))\n.expect(\"an edit target must still be present\")\n// after\nmatch path.resolve(&self.root) {\n    Some(element) => element,\n    None => return None, // or stdx::never! + safe fallback\n}","handlingStrategy":"fallback","validationCode":"// User-facing: ensure edits are applied only via the editor API and the root\n// is not replaced mid-batch.\nassert!(editor.root().ancestors().count() > 0, \"root must remain live during apply\");","typeGuard":null,"tryCatchPattern":"// Since this is a panic, isolate edit application:\nstd::panic::catch_unwind(AssertUnwindSafe(|| editor_map_element(&element)))\n    .map_err(|_| \"edit target lost; rebuild editor from fresh tree\")","preventionTips":["Do not replace the editor root while tracked edits are pending","Apply all edits through SyntaxEditor; avoid mixing raw tree mutations with tracked edits","Pin the rust-analyzer version and check its issue tracker before assuming user error — this expect should be unreachable from the public API"],"tags":["rust","syntax-editor","internal-invariant","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}