ianstormtaylor/slate · error · Error

Cannot find a descendant at path [${path}] in node: ${Scrubb

Error message

Cannot find a descendant at path [${path}] in node: ${Scrubber.stringify(
          root
        )}

What it means

Node.get() walks the tree following the numeric path, but a segment cannot be resolved — either an index is out of range, an intermediate node is a text node (which has no children), or the path is longer than the tree depth. Slate throws because a missing node cannot be represented as undefined at this API level.

Source

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

          return { ...node, text: before }
        })
      }

      if (Path.equals(path, start.path)) {
        modifyLeaf(newRoot, path, node => {
          const before = node.text.slice(start.offset)
          return { ...node, text: before }
        })
      }
    }

    return newRoot.children
  },

  get(root: Node, path: Path): Node {
    const node = Node.getIf(root, path)
    if (node === undefined) {
      throw new Error(
        `Cannot find a descendant at path [${path}] in node: ${Scrubber.stringify(
          root
        )}`
      )
    }
    return node
  },

  getIf(root: Node, path: Path): Node | undefined {
    let node = root

    for (let i = 0; i < path.length; i++) {
      const p = path[i]

      if (typeof p !== 'number') {
        throw new Error('Got non-numeric path index')
      }

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Re-derive paths from the current document right before use (e.g. re-read selection or re-find the node) instead of caching paths across mutations.
  2. Check existence first with Node.has(root, path) before calling Node.get.
  3. If operating during transforms, defer lookups until after normalization completes (e.g. after ChangeEditor.flush or outside a withNormalized call).
  4. Validate depth: ensure the path length does not exceed tree depth via Node.ancestor or nodes() traversal.

Example fix

// before
const node = Node.get(editor, stalePath)

// after
const node = Node.has(editor, stalePath) ? Node.get(editor, stalePath) : fallbackNode
Defensive patterns

Strategy: validation

Validate before calling

if (!Node.has(root, path)) { /* path is stale; re-derive it */ }

Type guard

const safeGet = (root: Node, path: Path): Node | null => Node.has(root, path) ? Node.get(root, path) : null

Try / catch

try { Node.get(root, path) } catch (e) { if (e instanceof Error && e.message.startsWith('Cannot find a descendant')) { /* re-derive path */ } throw e }

Prevention

When it happens

Trigger: Calling Node.get(editor, [1]) when the editor has only one child; calling Node.get with a stale path after a normalization/operation changed the tree; resolving a path that descends into a text node's 'children'.

Common situations: Using paths captured before an async operation (selection change, normalization, collaboritive edit) and applying them after the document mutated; desynchronized document between two editor instances; incorrect manual path arithmetic when building paths.

Related errors


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