ianstormtaylor/slate · error · Error

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

Error message

Cannot get the descendant node at path [${path}] because it refers to the root editor node instead: ${Scrubber.stringify(
          node
        )}

What it means

Node.descendant() attempts to resolve a path inside a node tree, but the resolved node is the editor itself. In Slate, the editor is the root container and not a 'descendant', so the API refuses to return it. This almost always means the path was empty ([]) or pointed back to the root.

Source

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

    while (reverse ? index >= 0 : index < children.length) {
      const child = Node.child(ancestor, index)
      const childPath = path.concat(index)
      yield [child, childPath]
      index = reverse ? index - 1 : index + 1
    }
  },

  common(root: Node, path: Path, another: Path): NodeEntry {
    const p = Path.common(path, another)
    const n = Node.get(root, p)
    return [n, p]
  },

  descendant(root: Node, path: Path): Descendant {
    const node = Node.get(root, path)

    if (Node.isEditor(node)) {
      throw new Error(
        `Cannot get the descendant node at path [${path}] because it refers to the root editor node instead: ${Scrubber.stringify(
          node
        )}`
      )
    }

    return node
  },

  *descendants(
    root: Node,
    options: NodeDescendantsOptions = {}
  ): Generator<NodeEntry<Descendant>, void, undefined> {
    for (const [node, path] of Node.nodes(root, options)) {
      if (path.length !== 0) {
        // NOTE: we have to coerce here because checking the path's length does
        // guarantee that `node` is not a `Editor`, but TypeScript doesn't know.
        yield [node, path] as NodeEntry<Descendant>

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Check that the path is non-empty and does not resolve to the root before calling descendant: if (path.length === 0) handle root explicitly.
  2. If you actually want the root, use the editor object directly instead of Node.descendant.
  3. Use Node.get with your own Editor.isEditor check if you need the root case to be handled generically.
  4. Verify Path.parent() results: it throws only on [], so guard paths of length 1 before descending further.

Example fix

// before
const node = Node.descendant(editor, Path.parent(somePath))

// after
const p = Path.parent(somePath)
const node = p.length === 0 ? editor : Node.descendant(editor, p)
Defensive patterns

Strategy: type-guard

Validate before calling

if (path.length === 0) { /* root: use editor directly */ }

Type guard

const isNonRootPath = (p: Path): boolean => p.length > 0 && !Editor.isEditor(Node.getIf(editor, p))

Prevention

When it happens

Trigger: Calling Node.descendant(editor, []) or Editor.node() with a path of length 0; passing a path computed from Path.parent on a top-level path ([0] -> []); passing an editor where a descendant node was expected.

Common situations: Walking up with Path.parent until the root and then calling descendant on the result; off-by-one in path math that produces an empty path; calling Editor.node(editor, path) where path was normalized to the root.

Related errors


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