Skip to content

multi_buffer: Fix "cannot seek backward" crash in summaries_for_anchors#49047

Merged
Veykril merged 2 commits intomainfrom
crashbot
Feb 14, 2026
Merged

multi_buffer: Fix "cannot seek backward" crash in summaries_for_anchors#49047
Veykril merged 2 commits intomainfrom
crashbot

Conversation

@eholk
Copy link
Contributor

@eholk eholk commented Feb 12, 2026

Bug

Sentry: ZED-3W3 (13 events, first seen 2025-12-03)

summaries_for_anchors panics with "cannot seek backward" when iterating selection anchors whose excerpt locators are no longer in ascending order after an update_path_excerpts call.

Root cause

update_path_excerpts merges new excerpt ranges with existing ones. When an existing excerpt has no overlap with any new range (e.g. existing_range.end < new.context.start), it is removed from the excerpts tree but is not recorded in replaced_excerpts. Its stale locator persists in the excerpt_ids tree.

Later, when a new excerpt is inserted for a different path whose locator falls between the stale locator and another surviving excerpt, summaries_for_anchors can encounter anchors in an order where it needs to seek the cursor backward — violating the forward-only cursor invariant.

Concretely, the scenario requires:

  1. A path with ≥3 excerpts (E_B1, E_B2, E_B3) with anchors in E_B2 and E_B3.
  2. An update_path_excerpts call that keeps E_B1, removes E_B2 (no overlap → no replaced_excerpts entry), and replaces E_B3 with a new excerpt N.
  3. A new path D inserted between paths B and C, whose excerpt E_D gets a locator between N and E_B2's stale locator.
  4. Resolving the old anchors: E_B2's stale locator seeks past E_D, then E_B3→N tries to seek backward to N — panic.

Fix

In the two branches of update_path_excerpts where existing excerpts are removed without being absorbed into a new range, map the removed excerpt to the nearest surviving neighbor (the last kept or inserted excerpt) in replaced_excerpts. This ensures stale anchors always remap to a valid, correctly-ordered locator.

The two branches are:

  • existing_range.end < new.context.start — existing excerpt falls entirely before the next new range
  • (None, Some(...)) — no more new ranges for remaining existing excerpts

Verification

  • A reproduction test (test_cannot_seek_backward_after_excerpt_replacement) that constructs the exact crash scenario now passes.
  • Full multi_buffer test suite (48 tests) passes with no regressions.
  • Clippy is clean.

Release Notes:

  • Fixed a possible crash when updating excerpts in multibuffers

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Feb 12, 2026
@zed-community-bot zed-community-bot bot added the staff Pull requests authored by a current member of Zed staff label Feb 12, 2026
@zed-industries-bot
Copy link
Contributor

zed-industries-bot commented Feb 12, 2026

Warnings
⚠️

This PR is missing release notes.

Please add a "Release Notes" section that describes the change:

Release Notes:

- Added/Fixed/Improved ...

If your change is not user-facing, you can use "N/A" for the entry:

Release Notes:

- N/A

Generated by 🚫 dangerJS against 129afe6

@eholk eholk marked this pull request as ready for review February 13, 2026 19:25
@eholk eholk requested a review from cole-miller February 13, 2026 19:25
@Veykril Veykril merged commit 0f84a36 into main Feb 14, 2026
28 checks passed
@Veykril Veykril deleted the crashbot branch February 14, 2026 08:19
maxdeviant added a commit that referenced this pull request Feb 15, 2026
maxdeviant added a commit that referenced this pull request Feb 15, 2026
…or_anchors (#49047)" (#49238)

This reverts commit 0f84a36.

We're reverting #49047, as it has led to different kinds of panics.

Release Notes:

- Reverted #49047.
Veykril added a commit that referenced this pull request Feb 16, 2026
…der (#49290)

Second attempt at fixing
#49047, this also removes some
of the sorting hacks as they should ideally not be necessary anymore
after this fix

Release Notes:

- N/A *or* Added/Fixed/Improved ...
@github-actions github-actions bot mentioned this pull request Feb 17, 2026
5 tasks
rtfeldman pushed a commit that referenced this pull request Feb 17, 2026
…rs (#49047)

## Bug

**Sentry:**
[ZED-3W3](https://sentry.io/organizations/zed-dev/issues/7085300207/)
(13 events, first seen 2025-12-03)

`summaries_for_anchors` panics with "cannot seek backward" when
iterating selection anchors whose excerpt locators are no longer in
ascending order after an `update_path_excerpts` call.

### Root cause

`update_path_excerpts` merges new excerpt ranges with existing ones.
When an existing excerpt has no overlap with any new range (e.g.
`existing_range.end < new.context.start`), it is removed from the
excerpts tree but is **not** recorded in `replaced_excerpts`. Its stale
locator persists in the `excerpt_ids` tree.

Later, when a new excerpt is inserted for a different path whose locator
falls between the stale locator and another surviving excerpt,
`summaries_for_anchors` can encounter anchors in an order where it needs
to seek the cursor *backward* — violating the forward-only cursor
invariant.

Concretely, the scenario requires:
1. A path with ≥3 excerpts (E_B1, E_B2, E_B3) with anchors in E_B2 and
E_B3.
2. An `update_path_excerpts` call that keeps E_B1, **removes E_B2** (no
overlap → no `replaced_excerpts` entry), and replaces E_B3 with a new
excerpt N.
3. A new path D inserted between paths B and C, whose excerpt E_D gets a
locator between N and E_B2's stale locator.
4. Resolving the old anchors: E_B2's stale locator seeks past E_D, then
E_B3→N tries to seek backward to N — **panic**.

## Fix

In the two branches of `update_path_excerpts` where existing excerpts
are removed without being absorbed into a new range, map the removed
excerpt to the nearest surviving neighbor (the last kept or inserted
excerpt) in `replaced_excerpts`. This ensures stale anchors always remap
to a valid, correctly-ordered locator.

The two branches are:
- `existing_range.end < new.context.start` — existing excerpt falls
entirely before the next new range
- `(None, Some(...))` — no more new ranges for remaining existing
excerpts

## Verification

- A reproduction test
(`test_cannot_seek_backward_after_excerpt_replacement`) that constructs
the exact crash scenario now passes.
- Full `multi_buffer` test suite (48 tests) passes with no regressions.
- Clippy is clean.

Release Notes:
- Fixed a possible crash when updating excerpts in multibuffers
rtfeldman pushed a commit that referenced this pull request Feb 17, 2026
…or_anchors (#49047)" (#49238)

This reverts commit 0f84a36.

We're reverting #49047, as it has led to different kinds of panics.

Release Notes:

- Reverted #49047.
rtfeldman pushed a commit that referenced this pull request Feb 17, 2026
…der (#49290)

Second attempt at fixing
#49047, this also removes some
of the sorting hacks as they should ideally not be necessary anymore
after this fix

Release Notes:

- N/A *or* Added/Fixed/Improved ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement staff Pull requests authored by a current member of Zed staff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants