Fix position bounds check in updateDraggedNode #190

Closed
dougrathbone wants to merge 1 commit from fix-update-dragged-node-bounds-check into master
dougrathbone commented 2026-03-17 11:42:07 +01:00 (Migrated from github.com)

Problem

When a document changes size during a drag operation (e.g. via collaborative editing or an external state update), the stored node selection position (sel.from) can exceed the new document's content size. Calling doc.nodeAt() with an out-of-range position causes Fragment.findIndex to throw:

RangeError: Position X outside of fragment (...)

This crash originates in updateDraggedNode (called from updateStateInner) where this.state.doc.nodeAt(sel.from) is called without verifying that sel.from is within the document's valid range.

Reproduction

This occurs in collaborative editing environments where:

  1. User starts dragging a node
  2. A remote edit arrives that changes the document size (deleting content)
  3. updateStateInner is called with the new state
  4. updateDraggedNode tries to look up the dragged node at the original position, which is now out of range

We've observed this affecting ~161 events across 7 users in our production collaborative editor (Dovetail, built on ProseMirror with Yjs).

Fix

Added bounds checks before both nodeAt calls in updateDraggedNode:

// Before:
if (this.state.doc.nodeAt(sel.from) == sel.node) {

// After:
if (sel.from < this.state.doc.content.size && this.state.doc.nodeAt(sel.from) == sel.node) {

And similarly for the movedPos fallback path.

When the position is out of range, the method correctly falls through to found = -1, which sets this.dragging.node to undefined — the existing behavior for when the dragged node can't be found.

Version

Tested against prosemirror-view@1.32.7 but the same code exists on master.

## Problem When a document changes size during a drag operation (e.g. via collaborative editing or an external state update), the stored node selection position (`sel.from`) can exceed the new document's content size. Calling `doc.nodeAt()` with an out-of-range position causes `Fragment.findIndex` to throw: ``` RangeError: Position X outside of fragment (...) ``` This crash originates in `updateDraggedNode` (called from `updateStateInner`) where `this.state.doc.nodeAt(sel.from)` is called without verifying that `sel.from` is within the document's valid range. ### Reproduction This occurs in collaborative editing environments where: 1. User starts dragging a node 2. A remote edit arrives that changes the document size (deleting content) 3. `updateStateInner` is called with the new state 4. `updateDraggedNode` tries to look up the dragged node at the original position, which is now out of range We've observed this affecting ~161 events across 7 users in our production collaborative editor (Dovetail, built on ProseMirror with Yjs). ### Fix Added bounds checks before both `nodeAt` calls in `updateDraggedNode`: ```typescript // Before: if (this.state.doc.nodeAt(sel.from) == sel.node) { // After: if (sel.from < this.state.doc.content.size && this.state.doc.nodeAt(sel.from) == sel.node) { ``` And similarly for the `movedPos` fallback path. When the position is out of range, the method correctly falls through to `found = -1`, which sets `this.dragging.node` to `undefined` — the existing behavior for when the dragged node can't be found. ### Version Tested against `prosemirror-view@1.32.7` but the same code exists on `master`.
marijnh commented 2026-03-17 13:49:00 +01:00 (Migrated from github.com)

Firstly, you reformatted the entire file. You're going to want to submit a patch with only the actual changes you made.

Secondly, can you confirm that you did not use AI to generate this patch?

Firstly, you reformatted the entire file. You're going to want to submit a patch with only the actual changes you made. Secondly, can you confirm that you did not use AI to generate this patch?
dougrathbone commented 2026-03-17 21:24:04 +01:00 (Migrated from github.com)

Closing to resubmit with only the bug fix - apologies for the reformatting.

Closing to resubmit with only the bug fix - apologies for the reformatting.

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
prosemirror/prosemirror-view!190
No description provided.