ianstormtaylor/slate · error · Error

Cannot get the ${edge} point in the node at path [${at}] bec

Error message

Cannot get the ${edge} point in the node at path [${at}] because it has no ${edge} text node.

What it means

Editor.point(editor, at, { edge }) computes the start or end point of a node by finding its first/last text node. If the node at that path is not (or does not contain) a text node — e.g. an empty element with no children — the edge point cannot be computed, so it throws.

Source

Thrown at packages/slate/src/editor/point.ts:23

export const point: EditorInterface['point'] = (editor, at, options = {}) => {
  const { edge = 'start' } = options

  if (Location.isPath(at)) {
    let path

    if (edge === 'end') {
      const [, lastPath] = Node.last(editor, at)
      path = lastPath
    } else {
      const [, firstPath] = Node.first(editor, at)
      path = firstPath
    }

    const node = Node.get(editor, path)

    if (!Node.isText(node)) {
      throw new Error(
        `Cannot get the ${edge} point in the node at path [${at}] because it has no ${edge} text node.`
      )
    }

    return { path, offset: edge === 'end' ? node.text.length : 0 }
  }

  if (Location.isRange(at)) {
    const [start, end] = Range.edges(at)
    return edge === 'start' ? start : end
  }

  return at
}

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Ensure elements always contain at least one empty text node ({ text: '' }) — Slate's default normalization normally enforces this, so check custom normalizers/transforms that empty children
  2. Guard with Editor.hasPath or check Node.get(...).children?.length before computing the point
  3. Use editor.selection or Range.end on an existing valid range instead of deriving a point from an empty node

Example fix

// before
Transforms.removeNodes(editor, { at: [0, 0] }) // leaves element empty
const end = Editor.end(editor, [0]) // throws

// after
Transforms.removeNodes(editor, { at: [0, 0] })
if (Node.get(editor, [0]).children.length === 0) {
  Transforms.insertNodes(editor, { text: '' }, { at: [0, 0] })
}
const end = Editor.end(editor, [0])
Defensive patterns

Strategy: validation

Validate before calling

const node = Node.get(editor, path)
if (!Node.isText(node) && node.children?.length > 0) {
  const end = Editor.end(editor, path)
}

Type guard

const hasTextDescendant = (n) =>
  !Node.isText(n) && n.children.some(c => Node.isText(c) || c.children?.length)

Prevention

When it happens

Trigger: Calling Editor.point(editor, path, { edge: 'start' }) where the node at path is an empty element (children: []); calling Editor.start/Editor.end on a void element rendered with no text; passing a path deeper than the actual structure so Node.get resolves to a non-text node.

Common situations: Inserting a new empty block and immediately computing its end point to place the caret; normalizers creating empty elements; selections referencing nodes emptied by deletion.

Related errors


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