{"record":{"id":"59221e74078b8670","repo":"ianstormtaylor/slate","slug":"cannot-get-the-leaf-node-at-path-path-because","errorCode":null,"errorMessage":"Cannot get the leaf node at path [${path}] because it refers to a non-leaf node: ${Scrubber.stringify(\n          node\n        )}","messagePattern":"Cannot get the leaf node at path \\[(.+?)\\] because it refers to a non-leaf node: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/slate/src/interfaces/node.ts","lineNumber":528,"sourceCode":"\n    while (n) {\n      if (Node.isText(n) || n.children.length === 0) {\n        break\n      } else {\n        const i = n.children.length - 1\n        n = n.children[i]\n        p.push(i)\n      }\n    }\n\n    return [n, p]\n  },\n\n  leaf(root: Node, path: Path): Text {\n    const node = Node.get(root, path)\n\n    if (!Node.isText(node)) {\n      throw new Error(\n        `Cannot get the leaf node at path [${path}] because it refers to a non-leaf node: ${Scrubber.stringify(\n          node\n        )}`\n      )\n    }\n\n    return node\n  },\n\n  *levels(\n    root: Node,\n    path: Path,\n    options: NodeLevelsOptions = {}\n  ): Generator<NodeEntry, void, undefined> {\n    for (const p of Path.levels(path, options)) {\n      const n = Node.get(root, p)\n      yield [n, p]\n    }","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/ianstormtaylor/slate/blob/72a37c701e5da4bb13a305d416d9c070d19d3a45/packages/slate/src/interfaces/node.ts#L510-L546","documentation":"Node.leaf() returns the text node at a path, but the resolved node is an element (or the editor), not a Text node. Leaf in Slate strictly means a text node with no children, so this throws when the path points at a branch node.","triggerScenarios":"Calling Node.leaf(editor, path) where path points to an element node (e.g. a paragraph); using a path from Editor.nodes() with mode: 'highest'/'lowest' incorrectly; computing a leaf path from a Range.anchor.path of an element-level selection.","commonSituations":"Assuming selection.anchor.path always points at text when the selection is at element level; iterating nodes and calling leaf on each path without checking node kind; off-by-one where the path stops one level too early (missing the text child index).","solutions":["Check the node kind first: const n = Node.get(root, path); if (Text.isText(n)) ... else descend to its first text via Node.leaf(root, [...path, 0]) or Editor.leaf.","Use Node.texts(root, { from, to }) or Editor.nodes with mode: 'lowest' to enumerate actual text paths.","For ranges, ensure anchor/focus paths point at text nodes — Slate guarantees this only for normalized selections; re-normalize or use Range.isNormalized.","Verify path length: a leaf path usually equals the depth of text nodes; compare against Node.texts() output for the same node."],"exampleFix":"// before\nconst text = Node.leaf(editor, elementPath) // throws if elementPath is an element\n\n// after\nconst node = Node.get(editor, elementPath)\nconst text = Text.isText(node) ? node : Node.leaf(editor, [...elementPath, 0])","handlingStrategy":"validation","validationCode":"const node = Node.getIf(root, path)\nif (!node || !Text.isText(node)) { /* resolve a real text path instead */ }","typeGuard":"const isTextPath = (root: Node, path: Path): boolean => { const n = Node.getIf(root, path); return n !== undefined && Text.isText(n) }","tryCatchPattern":null,"preventionTips":["Use Node.texts / Editor.nodes mode 'lowest' to enumerate text paths.","Check Text.isText(Node.get(...)) before calling leaf.","Extend paths one level ([...path, 0]) when you know the node is an element."],"tags":["slate","path","text-node","leaf"],"backgroundTag":"slate-invalid-path","analyzedSha":"72a37c701e5da4bb13a305d416d9c070d19d3a45","analyzedAt":"2026-08-27T23:04:51.145Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}