{"record":{"id":"d1b5a809f5ccaca1","repo":"zed-industries/zed","slug":"anchor-s-path-was-never-added-to-multibuffer","errorCode":null,"errorMessage":"anchor's path was never added to multibuffer","messagePattern":"anchor's path was never added to multibuffer","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/multi_buffer/src/anchor.rs","lineNumber":107,"sourceCode":"}\n\nimpl ExcerptAnchor {\n    pub(crate) fn buffer_id(&self) -> BufferId {\n        self.text_anchor.buffer_id\n    }\n\n    pub(crate) fn text_anchor(&self) -> text::Anchor {\n        self.text_anchor\n    }\n\n    pub(crate) fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self {\n        self.diff_base_anchor = Some(diff_base_anchor);\n        self\n    }\n\n    pub(crate) fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {\n        let Some(self_path_key) = snapshot.path_keys.get_index(self.path.0 as usize) else {\n            panic!(\"anchor's path was never added to multibuffer\")\n        };\n        let Some(other_path_key) = snapshot.path_keys.get_index(other.path.0 as usize) else {\n            panic!(\"anchor's path was never added to multibuffer\")\n        };\n\n        match self_path_key.cmp(other_path_key) {\n            Ordering::Equal => (),\n            ordering => return ordering,\n        }\n\n        // in the case that you removed the buffer containing self,\n        // and added the buffer containing other with the same path key\n        // (ordering is arbitrary but consistent)\n        if self.text_anchor.buffer_id != other.text_anchor.buffer_id {\n            return self.text_anchor.buffer_id.cmp(&other.text_anchor.buffer_id);\n        }\n\n        // two anchors into the same buffer at the same path","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/multi_buffer/src/anchor.rs#L89-L125","documentation":"MultiBuffer anchors store a path key index into the snapshot's path_keys set, assigned when the anchor's buffer is added. When two anchors are compared (Anchor::cmp, used by sorting and cursor seeking), each anchor's path index is looked up in the snapshot; this panic (line 107) fires when the first anchor's index is absent - meaning the anchor does not belong to this snapshot. Usually it is a stale anchor whose buffer/path was removed after the snapshot was taken, or an anchor from a different multibuffer.","triggerScenarios":"Sorting or seeking with anchors against a MultiBufferSnapshot whose path_keys no longer contains the anchor's index: the buffer was removed (project search cleared results, a file was closed) while anchors created before the removal are still compared, or anchors from another multibuffer instance are mixed in.","commonSituations":"Project-wide search replacing all buffers at once while cursors/selections still hold old anchors; multibuffers containing several buffers that share a path; reusing cached snapshots after edits that remove excerpts; tests that build anchors against snapshot A and compare them via snapshot B.","solutions":["Take a fresh multibuffer.snapshot(cx) after any buffer removal and re-create/refresh anchors from it before comparing","Drop or recreate anchors whose buffer no longer exists in the snapshot (check buffer_id membership) instead of comparing them","Ensure anchors are only ever compared within the multibuffer instance that created them","If you control the call path, validate the path index and skip/log instead of panicking"],"exampleFix":"// before: anchors from an older snapshot get sorted\nanchors.sort_by(|a, b| a.cmp(b, &old_snapshot)); // panics if a buffer was removed\n\n// after: refresh the snapshot and keep only live anchors\nlet snapshot = multi_buffer.read(cx).snapshot(cx);\nlet live: Vec<_> = anchors\n    .into_iter()\n    .filter(|anchor| snapshot.buffer_ids().contains(&anchor.buffer_id))\n    .collect();\nlive.sort_by(|a, b| a.cmp(b, &snapshot));","handlingStrategy":"validation","validationCode":"// discard anchors that no longer resolve in this snapshot before comparing\nfn anchor_is_live(anchor: &Anchor, snapshot: &MultiBufferSnapshot) -> bool {\n    snapshot.buffer_ids().contains(&anchor.buffer_id)\n}","typeGuard":"fn live_anchors(\n    anchors: impl IntoIterator<Item = Anchor>,\n    snapshot: &MultiBufferSnapshot,\n) -> Vec<Anchor> {\n    anchors\n        .into_iter()\n        .filter(|anchor| snapshot.buffer_ids().contains(&anchor.buffer_id))\n        .collect()\n}","tryCatchPattern":null,"preventionTips":["Re-clone the multibuffer snapshot immediately before sorting or seeking anchors","Rebuild cursors and selections from fresh anchors after edits that remove buffers","Never share anchors between different MultiBuffer instances","Add debug assertions that every anchor's buffer exists in the snapshot before calling cmp"],"tags":["zed","multi-buffer","anchors","snapshot","panic"],"backgroundTag":"stale-snapshot-anchor","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}