ianstormtaylor/slate · error · Error

Cannot modify the editor

Error message

Cannot modify the editor

What it means

Internal utility modifyDescendant throws when asked to modify a descendant at the root path []. The editor root itself is not a descendant — modification of the root must go through the editor object, not this helper. Reaching this error usually means a transform was given an empty path where a child path was required.

Source

Thrown at packages/slate/src/utils/modify.ts:35

export const replaceChildren = <T>(
  xs: T[],
  index: number,
  removeCount: number,
  ...newValues: T[]
) => [...xs.slice(0, index), ...newValues, ...xs.slice(index + removeCount)]

export const removeChildren = replaceChildren

/**
 * Replace a descendant with a new node, replacing all ancestors
 */
export const modifyDescendant = <N extends Descendant>(
  root: Ancestor,
  path: Path,
  f: (node: N) => N
) => {
  if (path.length === 0) {
    throw new Error('Cannot modify the editor')
  }

  const node = Node.get(root, path) as N
  const slicedPath = path.slice()
  let modifiedNode: Node = f(node)

  while (slicedPath.length > 1) {
    const index = slicedPath.pop()!
    const ancestorNode = Node.get(root, slicedPath) as Ancestor

    modifiedNode = {
      ...ancestorNode,
      children: replaceChildren(ancestorNode.children, index, 1, modifiedNode),
    }
  }

  const index = slicedPath.pop()!
  root.children = replaceChildren(root.children, index, 1, modifiedNode)

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Ensure the path passed to node transforms points at a real descendant (length >= 1), not the root
  2. Debug the Path.parent/Path.previous chain that produced the empty path and handle the root case separately
  3. For root-level changes, use editor.children mutation or Editor-level APIs instead

Example fix

// before
const p = Path.parent(somePath) // may be []
Transforms.setNodes(editor, props, { at: p })

// after
const p = Path.parent(somePath)
if (p.length > 0) {
  Transform.setNodes(editor, props, { at: p })
} else {
  // handle root-level case explicitly
}
Defensive patterns

Strategy: validation

Validate before calling

if (path.length === 0) {
  // root: handle explicitly, never pass [] to node transforms
  return
}
Transforms.setNodes(editor, props, { at: path })

Type guard

const isDescendantPath = (p: Path): boolean => Array.isArray(p) && p.length > 0

Prevention

When it happens

Trigger: Internal transforms (insertNodes, removeNodes, setNodes etc.) routed through modifyDescendant with path = []; a Path computation (Path.parent of a depth-1 path) returning [] and then used as a descendant path.

Common situations: Custom transforms computing paths with Path.parent and passing [] into node-modifying transforms; bugs in path arithmetic after deleting root-level nodes; applying ops with empty paths.

Related errors


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