zed-industries/zed · error
anchor's path was never added to multibuffer
Error message
anchor's path was never added to multibuffer
What it means
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.
Source
Thrown at crates/multi_buffer/src/anchor.rs:107
}
impl ExcerptAnchor {
pub(crate) fn buffer_id(&self) -> BufferId {
self.text_anchor.buffer_id
}
pub(crate) fn text_anchor(&self) -> text::Anchor {
self.text_anchor
}
pub(crate) fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self {
self.diff_base_anchor = Some(diff_base_anchor);
self
}
pub(crate) fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {
let Some(self_path_key) = snapshot.path_keys.get_index(self.path.0 as usize) else {
panic!("anchor's path was never added to multibuffer")
};
let Some(other_path_key) = snapshot.path_keys.get_index(other.path.0 as usize) else {
panic!("anchor's path was never added to multibuffer")
};
match self_path_key.cmp(other_path_key) {
Ordering::Equal => (),
ordering => return ordering,
}
// in the case that you removed the buffer containing self,
// and added the buffer containing other with the same path key
// (ordering is arbitrary but consistent)
if self.text_anchor.buffer_id != other.text_anchor.buffer_id {
return self.text_anchor.buffer_id.cmp(&other.text_anchor.buffer_id);
}
// two anchors into the same buffer at the same pathView on GitHub (pinned to f4178619ac)
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
Example fix
// before: anchors from an older snapshot get sorted
anchors.sort_by(|a, b| a.cmp(b, &old_snapshot)); // panics if a buffer was removed
// after: refresh the snapshot and keep only live anchors
let snapshot = multi_buffer.read(cx).snapshot(cx);
let live: Vec<_> = anchors
.into_iter()
.filter(|anchor| snapshot.buffer_ids().contains(&anchor.buffer_id))
.collect();
live.sort_by(|a, b| a.cmp(b, &snapshot)); Defensive patterns
Strategy: validation
Validate before calling
// discard anchors that no longer resolve in this snapshot before comparing
fn anchor_is_live(anchor: &Anchor, snapshot: &MultiBufferSnapshot) -> bool {
snapshot.buffer_ids().contains(&anchor.buffer_id)
} Type guard
fn live_anchors(
anchors: impl IntoIterator<Item = Anchor>,
snapshot: &MultiBufferSnapshot,
) -> Vec<Anchor> {
anchors
.into_iter()
.filter(|anchor| snapshot.buffer_ids().contains(&anchor.buffer_id))
.collect()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- {:?} is in non-existent deleted hunk
- row out of range
- invalid display row {}
- blocking sender returned without value
- byte index {} is out of bounds of rope (length: {})
AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20).
Data as JSON: /api/errors/d1b5a809f5ccaca1.
Report an issue: GitHub.