ianstormtaylor/slate · error · Error

Cannot get the leaf node at path [${path}] because it refers

Error message

Cannot get the leaf node at path [${path}] because it refers to a non-leaf node: ${Scrubber.stringify(
          node
        )}

What it means

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.

Source

Thrown at packages/slate/src/interfaces/node.ts:528

    while (n) {
      if (Node.isText(n) || n.children.length === 0) {
        break
      } else {
        const i = n.children.length - 1
        n = n.children[i]
        p.push(i)
      }
    }

    return [n, p]
  },

  leaf(root: Node, path: Path): Text {
    const node = Node.get(root, path)

    if (!Node.isText(node)) {
      throw new Error(
        `Cannot get the leaf node at path [${path}] because it refers to a non-leaf node: ${Scrubber.stringify(
          node
        )}`
      )
    }

    return node
  },

  *levels(
    root: Node,
    path: Path,
    options: NodeLevelsOptions = {}
  ): Generator<NodeEntry, void, undefined> {
    for (const p of Path.levels(path, options)) {
      const n = Node.get(root, p)
      yield [n, p]
    }

View on GitHub (pinned to 72a37c701e)

Solutions

  1. 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.
  2. Use Node.texts(root, { from, to }) or Editor.nodes with mode: 'lowest' to enumerate actual text paths.
  3. For ranges, ensure anchor/focus paths point at text nodes — Slate guarantees this only for normalized selections; re-normalize or use Range.isNormalized.
  4. Verify path length: a leaf path usually equals the depth of text nodes; compare against Node.texts() output for the same node.

Example fix

// before
const text = Node.leaf(editor, elementPath) // throws if elementPath is an element

// after
const node = Node.get(editor, elementPath)
const text = Text.isText(node) ? node : Node.leaf(editor, [...elementPath, 0])
Defensive patterns

Strategy: validation

Validate before calling

const node = Node.getIf(root, path)
if (!node || !Text.isText(node)) { /* resolve a real text path instead */ }

Type guard

const isTextPath = (root: Node, path: Path): boolean => { const n = Node.getIf(root, path); return n !== undefined && Text.isText(n) }

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


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