ianstormtaylor/slate · error · Error

Cannot get the previous path of a root path [${path}], becau

Error message

Cannot get the previous path of a root path [${path}], because it has no previous index.

What it means

Path.previous() returns the preceding sibling path by decrementing the last index. The root path [] has no last index, so there is no previous sibling and Slate throws. Note this checks only the root; a path like [0] returns [-1], which is invalid but does not throw here — bounds checks are the caller's job.

Source

Thrown at packages/slate/src/interfaces/path.ts:366

      case 'split_node':
      case 'move_node':
        return true
      default:
        return false
    }
  },

  parent(path: Path): Path {
    if (path.length === 0) {
      throw new Error(`Cannot get the parent path of the root path [${path}].`)
    }

    return path.slice(0, -1)
  },

  previous(path: Path): Path {
    if (path.length === 0) {
      throw new Error(
        `Cannot get the previous path of a root path [${path}], because it has no previous index.`
      )
    }

    const last = path[path.length - 1]

    if (last <= 0) {
      throw new Error(
        `Cannot get the previous path of a first child path [${path}] because it would result in a negative index.`
      )
    }

    return path.slice(0, -1).concat(last - 1)
  },

  relative(path: Path, ancestor: Path): Path {
    if (!Path.isAncestor(ancestor, path) && !Path.equals(path, ancestor)) {
      throw new Error(

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Guard before decrementing: if (path.length === 0) stop — and also stop when the last index reaches 0 to avoid producing [-1].
  2. Bound reverse loops by the first sibling: while (lastIndex >= 0) { visit(...path, lastIndex); lastIndex-- } or compare against Path.parent's children length.
  3. Prefer built-in traversals (Node.nodes with reverse: true) which handle sibling boundaries correctly.
  4. Add explicit root checks in any code that chains Path.parent then Path.previous.

Example fix

// before
let p = somePath
while (true) { visit(p); p = Path.previous(p) } // throws on []

// after
let p = somePath
while (p.length > 0 && p[p.length - 1] >= 0 && Editor.hasPath(editor, p)) {
  visit(p)
  p = Path.previous(p)
}
Defensive patterns

Strategy: validation

Validate before calling

if (path.length === 0 || path[path.length - 1] === 0) { /* no previous sibling */ }

Type guard

const hasPrevSibling = (p: Path): boolean => p.length > 0 && p[p.length - 1] > 0

Prevention

When it happens

Trigger: Calling Path.previous([]) directly; reverse iteration walking siblings upward without a root guard; calling Path.previous(Path.parent(p)) when p had length 1, yielding the root path first.

Common situations: Iterating siblings backwards (for (let q = last; ; q = Path.previous(q))) without stopping at the first sibling or the root; loop conditions that assume previous() returns null/undefined at the boundary instead of throwing; porting traversal code that used next() and forgetting the asymmetry.

Related errors


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