{"record":{"id":"59a48baadaf3cb71","repo":"ianstormtaylor/slate","slug":"cannot-apply-a-merge-node-operation-at-path-p","errorCode":null,"errorMessage":"Cannot apply a \"merge_node\" operation at path [${path}] because the root node cannot be merged.","messagePattern":"Cannot apply a \"merge_node\" operation at path \\[(.+?)\\] because the root node cannot be merged\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/slate/src/interfaces/transforms/general.ts","lineNumber":98,"sourceCode":"\n          return {\n            ...node,\n            text: before + text + after,\n          }\n        })\n\n        transformSelection = true\n        break\n      }\n\n      case 'merge_node': {\n        const { path } = op\n        const index = path[path.length - 1]\n        const prevPath = Path.previous(path)\n        const prevIndex = prevPath[prevPath.length - 1]\n\n        if (path.length === 0) {\n          throw new Error(\n            `Cannot apply a \"merge_node\" operation at path [${path}] because the root node cannot be merged.`\n          )\n        }\n\n        // Defend against malicious paths containing strings\n        if (typeof index !== 'number' || typeof prevIndex !== 'number')\n          throw new Error('Index must be number')\n\n        modifyChildren(editor, Path.parent(path), children => {\n          const node = children[index]\n          const prev = children[prevIndex]\n          let newNode: Descendant\n\n          if (Node.isText(node) && Node.isText(prev)) {\n            newNode = { ...prev, text: prev.text + node.text }\n          } else if (Node.isElement(node) && Node.isElement(prev)) {\n            newNode = { ...prev, children: prev.children.concat(node.children) }\n          } else {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/ianstormtaylor/slate/blob/72a37c701e5da4bb13a305d416d9c070d19d3a45/packages/slate/src/interfaces/transforms/general.ts#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guard before merging: only merge when path.length > 0 and Path.previous() would be valid.","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).","Check the value of at in Transforms.mergeNodes — it may be resolving to [] unexpectedly."],"exampleFix":"// before\nTransforms.mergeNodes(editor, { at: path }) // path may be []\n\n// after\nif (path.length > 0 && path[path.length - 1] > 0) {\n  Transforms.mergeNodes(editor, { at: path })\n}","handlingStrategy":"validation","validationCode":"if (path.length > 0 && path[path.length - 1] > 0) {\n  Transforms.mergeNodes(editor, { at: path })\n}","typeGuard":"function isMergeablePath(path: Path): boolean {\n  return path.length > 0 && path[path.length - 1] > 0\n}","tryCatchPattern":null,"preventionTips":["Always pass an explicit non-root at to Transforms.mergeNodes.","Remember merging merges into the PREVIOUS sibling — first children can't merge.","Log the resolved path before merging to catch collapses to []."],"tags":["slate","merge-node","root-node","operation"],"backgroundTag":"operation-apply-failed","analyzedSha":"72a37c701e5da4bb13a305d416d9c070d19d3a45","analyzedAt":"2026-08-27T23:04:51.145Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}