ianstormtaylor/slate · error · Error

Cannot apply a "merge_node" operation at path [${path}] beca

Error message

Cannot apply a "merge_node" operation at path [${path}] because the root node cannot be merged.

What it means

A merge_node operation joins a node with its previous sibling. The root node (path []) has no previous sibling and no parent to merge into, so Slate throws when merging at the root.

Source

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

          return {
            ...node,
            text: before + text + after,
          }
        })

        transformSelection = true
        break
      }

      case 'merge_node': {
        const { path } = op
        const index = path[path.length - 1]
        const prevPath = Path.previous(path)
        const prevIndex = prevPath[prevPath.length - 1]

        if (path.length === 0) {
          throw new Error(
            `Cannot apply a "merge_node" operation at path [${path}] because the root node cannot be merged.`
          )
        }

        // Defend against malicious paths containing strings
        if (typeof index !== 'number' || typeof prevIndex !== 'number')
          throw new Error('Index must be number')

        modifyChildren(editor, Path.parent(path), children => {
          const node = children[index]
          const prev = children[prevIndex]
          let newNode: Descendant

          if (Node.isText(node) && Node.isText(prev)) {
            newNode = { ...prev, text: prev.text + node.text }
          } else if (Node.isElement(node) && Node.isElement(prev)) {
            newNode = { ...prev, children: prev.children.concat(node.children) }
          } else {

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Guard before merging: only merge when path.length > 0 and Path.previous() would be valid.
  2. If you meant to merge the first child into something, merge the SECOND child into the first instead (merging always merges a node into its previous sibling).
  3. Check the value of at in Transforms.mergeNodes — it may be resolving to [] unexpectedly.

Example fix

// before
Transforms.mergeNodes(editor, { at: path }) // path may be []

// after
if (path.length > 0 && path[path.length - 1] > 0) {
  Transforms.mergeNodes(editor, { at: path })
}
Defensive patterns

Strategy: validation

Validate before calling

if (path.length > 0 && path[path.length - 1] > 0) {
  Transforms.mergeNodes(editor, { at: path })
}

Type guard

function isMergeablePath(path: Path): boolean {
  return path.length > 0 && path[path.length - 1] > 0
}

Prevention

When it happens

Trigger: Applying or triggering merge_node with path === [] — e.g. Transforms.mergeNodes(editor, { at: [] }) or a computed path that collapses to the root, often after a path loses all segments during transforms.

Common situations: Custom delete/merge handlers that call Transforms.mergeNodes without an explicit non-root path; merging after other transforms shifted the target to the root; simulating Backspace at editor root.

Related errors


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