{"record":{"id":"218273dfcd5a6202","repo":"ianstormtaylor/slate","slug":"cannot-apply-a-merge-node-operation-at-path-p-218273","errorCode":null,"errorMessage":"Cannot apply a \"merge_node\" operation at path [${path}] to nodes of different interfaces: ${Scrubber.stringify(\n                node\n              )} ${Scrubber.stringify(prev)}","messagePattern":"Cannot apply a \"merge_node\" operation at path \\[(.+?)\\] to nodes of different interfaces: (.+?) (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/slate/src/interfaces/transforms/general.ts","lineNumber":117,"sourceCode":"            `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 {\n            throw new Error(\n              `Cannot apply a \"merge_node\" operation at path [${path}] to nodes of different interfaces: ${Scrubber.stringify(\n                node\n              )} ${Scrubber.stringify(prev)}`\n            )\n          }\n\n          return replaceChildren(children, prevIndex, 2, newNode)\n        })\n\n        transformSelection = true\n        break\n      }\n\n      case 'move_node': {\n        const { path, newPath } = op\n        const index = path[path.length - 1]\n\n        if (Path.isAncestor(path, newPath)) {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/ianstormtaylor/slate/blob/72a37c701e5da4bb13a305d416d9c070d19d3a45/packages/slate/src/interfaces/transforms/general.ts#L99-L135","documentation":"merge_node merges a node into its previous sibling, which only works when both are text nodes or both are element nodes. It throws when one is a text node and the other an element (or otherwise incompatible interfaces), since there is no defined way to merge them.","triggerScenarios":"Applying merge_node at a path whose node is a text node while the previous sibling is an element, or vice versa — e.g. merging path [1] where [0] is an element and [1] is a text node; often from hand-built operations or a document whose children violate Slate's content schema.","commonSituations":"Documents with mixed invalid children (element containing both element and text children directly where schema forbids it); custom normalizers that merge nodes without checking node types; replaying operations against a diverged document.","solutions":["Before merging, check that Node.isText matches for both the node and its previous sibling; if they differ, use Transforms.wrapNodes/unwrapNodes or remove one instead.","Run your editor with a schema/normalizer so parents only ever contain homogeneous mergeable children.","Validate the document structure after deserializing HTML/Markdown before applying merges."],"exampleFix":"// before\nTransforms.mergeNodes(editor, { at: path })\n\n// after\nconst node = Node.get(editor, path)\nconst prev = Node.get(editor, Path.previous(path))\nif (Node.isText(node) === Node.isText(prev)) {\n  Transforms.mergeNodes(editor, { at: path })\n} else {\n  Transforms.removeNodes(editor, { at: path })\n}","handlingStrategy":"type-guard","validationCode":"const node = Node.get(editor, path)\nconst prev = Node.get(editor, Path.previous(path))\nif (Node.isText(node) === Node.isText(prev)) {\n  Transforms.mergeNodes(editor, { at: path })\n}","typeGuard":"function areMergeable(a: Node, b: Node): boolean {\n  return (Node.isText(a) && Node.isText(b)) || (Node.isElement(a) && Node.isElement(b))\n}","tryCatchPattern":"try {\n  Transforms.mergeNodes(editor, { at: path })\n} catch (e) {\n  if (e instanceof Error && e.message.includes('different interfaces')) {\n    Transforms.removeNodes(editor, { at: path })\n  } else throw e\n}","preventionTips":["Enforce content schemas with withNormalization so children are homogeneous.","Validate deserialized documents before applying merge transforms.","Check node kinds before custom merge logic."],"tags":["slate","merge-node","node-types","invariant"],"backgroundTag":"operation-apply-failed","analyzedSha":"72a37c701e5da4bb13a305d416d9c070d19d3a45","analyzedAt":"2026-08-27T23:04:51.145Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}