Skip to content

fix: validate lineNumber bounds in hover computeSync (fixes #318008)#318018

Open
vs-code-engineering[bot] wants to merge 1 commit into
mainfrom
fix/hover-stale-lineNumber-318008-bdcb1f14baf8ee7b
Open

fix: validate lineNumber bounds in hover computeSync (fixes #318008)#318018
vs-code-engineering[bot] wants to merge 1 commit into
mainfrom
fix/hover-stale-lineNumber-318008-bdcb1f14baf8ee7b

Conversation

@vs-code-engineering
Copy link
Copy Markdown
Contributor

🔧 Error Fix

Summary

The hover operation uses a Debouncer that can deliver a stale anchor after the model content has changed. When lines are deleted between anchor creation and computeSync execution, the anchor's startLineNumber exceeds the model's line count, causing getLineMaxColumn to throw BugIndicatingError('Illegal value for lineNumber').

This error affects all platforms (Windows, Mac, Linux) on version 1.121.0 with 262 hits from 243 users.

Fixes #318008
Recommended reviewer: @alexdima

Culprit Commit

No single culprit commit identified. This is a latent race condition in the hover debounce architecture that has existed since the Debouncer was introduced for hover operations. The spike in telemetry correlates with the 1.121.0 stable release reaching more users rather than a specific code change.

Code Flow

sequenceDiagram
    participant User
    participant HC as HoverController
    participant HO as HoverOperation
    participant DB as Debouncer
    participant CHC as ContentHoverComputer
    participant MHP as MarkdownHoverParticipant
    participant TM as TextModel

    User->>HC: mouse hover (line 50)
    HC->>HO: start(Delayed, anchor{line:50})
    HO->>DB: schedule(anchor, firstWaitTime)
    User->>TM: delete lines (model now has 40 lines)
    HC->>HC: onDidChangeModelContent cancelScheduler
    Note over HC: Only cancels _reactToEditorMouseMoveRunner
    Note over DB: Debouncer still pending with stale anchor
    DB->>HO: _triggerAsyncComputation(anchor{line:50})
    HO->>DB: schedule syncComputation(secondWaitTime)
    DB->>HO: _triggerSyncComputation(anchor{line:50})
    HO->>CHC: computeSync(anchor{line:50})
    CHC->>MHP: computeSync(anchor{line:50})
    MHP->>TM: getLineMaxColumn(50)
    TM-->>MHP: THROW Illegal value for lineNumber
Loading

Affected Files

File Role
src/vs/editor/contrib/hover/browser/contentHoverComputer.ts Entry point for sync hover computation — receives stale anchor from debouncer
src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts Hover participant that calls getLineMaxColumn with stale lineNumber
src/vs/editor/contrib/hover/browser/contentHoverController.ts Listens for model content changes but only cancels mouse-move runner, not hover operation
src/vs/editor/common/model/textModel.ts Crash site — throws when lineNumber is out of bounds

Repro Steps

  1. Open any file in VS Code
  2. Hover over a line near the end of the file to trigger hover computation
  3. While the hover debounce timer is pending (within ~150ms), quickly delete multiple lines at the end of the file (e.g., select + delete)
  4. The debouncer fires computeSync with the now-stale anchor whose lineNumber exceeds the new line count
  5. getLineMaxColumn throws Illegal value for lineNumber

How the Fix Works

Chosen approach: Added a guard clause in contentHoverComputer.ts:computeSync that validates the anchor's startLineNumber is within bounds (>= 1 and <= model.getLineCount()) before passing it to participants. This is a guard at the data consumer boundary where stale data from the debouncer first meets the model API — fixing at the boundary where invalid data enters rather than at the crash site (textModel.ts). A redundant guard is also added in markdownHoverParticipant.ts:computeSync for defense in depth since it is the direct caller of getLineMaxColumn.

Alternatives considered:

  • Cancelling the hover operation on onDidChangeModelContent: would change visible behavior (hover disappears when typing elsewhere in the document), and is a larger architectural change with broader impact.
  • Adding try/catch around getLineMaxColumn: would silence the error without fixing the stale data flow, violating the principle of not masking errors from telemetry.

Recommended Owner

@alexdima — primary owner of the editor hover infrastructure and most recent contributor to markdownHoverParticipant.ts computeSync logic.

Generated by errors-fix · ● 51.7M ·

)

The hover operation uses a debouncer that can deliver a stale anchor after
the model content has changed. When lines are deleted, the anchor's
lineNumber may exceed the model's line count, causing getLineMaxColumn to
throw 'Illegal value for lineNumber'.

Add a guard in contentHoverComputer.computeSync to validate the anchor's
lineNumber is within bounds before passing it to participants. Also add a
redundant guard in markdownHoverParticipant.computeSync for defense.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 22, 2026 16:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@vs-code-engineering vs-code-engineering Bot requested review from alexdima and Copilot May 22, 2026 16:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@vs-code-engineering vs-code-engineering Bot marked this pull request as ready for review May 22, 2026 16:34
@vs-code-engineering vs-code-engineering Bot enabled auto-merge (squash) May 22, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Error] unhandlederror-Uncaught Error: Illegal value for lineNumber

2 participants