ianstormtaylor/slate · error · Error

Cannot resolve a Slate node from DOM node: ${domEl}

Error message

Cannot resolve a Slate node from DOM node: ${domEl}

What it means

Thrown by DOMEditor.toSlateNode when a DOM element cannot be mapped back to a Slate node — the element is not inside the editor's managed DOM (no data-slate-node ancestor with a known mapping).

Source

Thrown at packages/slate-dom/src/plugin/dom-editor.ts:688

    ) as HTMLElement
    const isEndAtZeroWidth = !!endEl.getAttribute('data-slate-zero-width')

    domRange.setStart(startNode, isStartAtZeroWidth ? 1 : startOffset)
    domRange.setEnd(endNode, isEndAtZeroWidth ? 1 : endOffset)
    return domRange
  },

  toSlateNode: (editor, domNode) => {
    let domEl = isDOMElement(domNode) ? domNode : domNode.parentElement

    if (domEl && !domEl.hasAttribute('data-slate-node')) {
      domEl = domEl.closest(`[data-slate-node]`)
    }

    const node = domEl ? ELEMENT_TO_NODE.get(domEl as HTMLElement) : null

    if (!node) {
      throw new Error(`Cannot resolve a Slate node from DOM node: ${domEl}`)
    }

    return node
  },

  toSlatePoint: <T extends boolean>(
    editor: DOMEditor,
    domPoint: DOMPoint,
    options: {
      exactMatch: boolean
      suppressThrow: T
      searchDirection?: 'forward' | 'backward'
    }
  ): T extends true ? Point | null : Point => {
    const { exactMatch, suppressThrow } = options
    const [nearestNode, nearestOffset] = exactMatch
      ? domPoint
      : normalizeDOMPoint(domPoint)

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Check DOMEditor.hasDOMNode(editor, el) before calling toSlateNode
  2. Use event.target.closest('[data-slate-node]') to ensure you start from a Slate-managed element
  3. For void element UI, use the element's data attributes or the void node context rather than toSlateNode on portals
  4. Verify you're using the correct editor instance

Example fix

// before
const node = DOMEditor.toSlateNode(editor, toolbarEl)
// after
if (DOMEditor.hasDOMNode(editor, toolbarEl)) {
  const node = DOMEditor.toSlateNode(editor, toolbarEl)
}
Defensive patterns

Strategy: type-guard

Validate before calling

const el = (event.target as HTMLElement).closest('[data-slate-node]')
if (el && DOMEditor.hasDOMNode(editor, el)) {
  const node = DOMEditor.toSlateNode(editor, el)
}

Type guard

const isSlateManagedEl = (editor: Editor, el: HTMLElement): boolean =>
  DOMEditor.hasDOMNode(editor, el)

Try / catch

try { DOMEditor.toSlateNode(editor, el) } catch (e) { if (/Cannot resolve a Slate node/.test(e.message)) ignore non-Slate DOM; else throw e }

Prevention

When it happens

Trigger: Passing an element from outside the Slate editable (toolbars, portals, other editors), or an element inside the editor whose WeakMap entry was cleared after unmount/re-render.

Common situations: Event handlers on custom void elements or menus passing their own container element to toSlateNode; multiple editors where the element belongs to the other instance; inspecting DOM after the editor remounted.

Related errors


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