ianstormtaylor/slate · error · Error

Cannot move a path [${path}] to new path [${newPath}] becaus

Error message

Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.

What it means

move_node relocates a node to newPath. It throws when the destination newPath is inside the subtree of the node being moved (i.e. path is an ancestor of newPath), since moving a node into itself is impossible and would corrupt the tree.

Source

Thrown at packages/slate/src/interfaces/transforms/general.ts:136

              `Cannot apply a "merge_node" operation at path [${path}] to nodes of different interfaces: ${Scrubber.stringify(
                node
              )} ${Scrubber.stringify(prev)}`
            )
          }

          return replaceChildren(children, prevIndex, 2, newNode)
        })

        transformSelection = true
        break
      }

      case 'move_node': {
        const { path, newPath } = op
        const index = path[path.length - 1]

        if (Path.isAncestor(path, newPath)) {
          throw new Error(
            `Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`
          )
        }

        const node = Node.get(editor, path)

        modifyChildren(editor, Path.parent(path), children =>
          removeChildren(children, index, 1)
        )

        // This is tricky, but since the `path` and `newPath` both refer to
        // the same snapshot in time, there's a mismatch. After either
        // removing the original position, the second step's path can be out
        // of date. So instead of using the `op.newPath` directly, we
        // transform `op.path` to ascertain what the `newPath` would be after
        // the operation was applied.
        const truePath = Path.transform(path, op)!
        const newIndex = truePath[truePath.length - 1]

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Before moving, reject or adjust when Path.isAncestor(path, newPath) or Path.equals(path, newPath).
  2. In drop handlers, filter out drop targets that are descendants of the dragged node (a common UX rule: can't drop into self).
  3. If the move target was computed against a pre-move document, transform the newPath with the move_node operation first.

Example fix

// before
Transforms.moveNodes(editor, { at: path, to: newPath })

// after
if (!Path.isAncestor(path, newPath) && !Path.equals(path, newPath)) {
  Transforms.moveNodes(editor, { at: path, to: newPath })
}
Defensive patterns

Strategy: validation

Validate before calling

if (!Path.isAncestor(path, newPath) && !Path.equals(path, newPath)) {
  Transforms.moveNodes(editor, { at: path, to: newPath })
}

Type guard

function isSafeMove(path: Path, newPath: Path): boolean {
  return !Path.isAncestor(path, newPath) && !Path.equals(path, newPath)
}

Prevention

When it happens

Trigger: Applying move_node with Path.isAncestor(path, newPath) true — e.g. moving [0] to [0,0] (into its own child). Typically from computed newPaths in drag-and-drop or nested-list reordering logic that doesn't exclude the dragged node's own subtree.

Common situations: Drag-and-drop implementations that allow dropping a container into its own descendants; nested list/block reordering where the new index is computed from the drop target without subtree checks; collaborative ops rebased incorrectly.

Related errors


AI-assisted analysis of ianstormtaylor/slate@72a37c701e (2026-08-27). Data as JSON: /api/errors/4da965909c2825aa. Report an issue: GitHub.