{"record":{"id":"e2b3d10321509eb4","repo":"ianstormtaylor/slate","slug":"cannot-resolve-a-slate-node-from-dom-node-domel","errorCode":null,"errorMessage":"Cannot resolve a Slate node from DOM node: ${domEl}","messagePattern":"Cannot resolve a Slate node from DOM node: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/slate-dom/src/plugin/dom-editor.ts","lineNumber":688,"sourceCode":"    ) as HTMLElement\n    const isEndAtZeroWidth = !!endEl.getAttribute('data-slate-zero-width')\n\n    domRange.setStart(startNode, isStartAtZeroWidth ? 1 : startOffset)\n    domRange.setEnd(endNode, isEndAtZeroWidth ? 1 : endOffset)\n    return domRange\n  },\n\n  toSlateNode: (editor, domNode) => {\n    let domEl = isDOMElement(domNode) ? domNode : domNode.parentElement\n\n    if (domEl && !domEl.hasAttribute('data-slate-node')) {\n      domEl = domEl.closest(`[data-slate-node]`)\n    }\n\n    const node = domEl ? ELEMENT_TO_NODE.get(domEl as HTMLElement) : null\n\n    if (!node) {\n      throw new Error(`Cannot resolve a Slate node from DOM node: ${domEl}`)\n    }\n\n    return node\n  },\n\n  toSlatePoint: <T extends boolean>(\n    editor: DOMEditor,\n    domPoint: DOMPoint,\n    options: {\n      exactMatch: boolean\n      suppressThrow: T\n      searchDirection?: 'forward' | 'backward'\n    }\n  ): T extends true ? Point | null : Point => {\n    const { exactMatch, suppressThrow } = options\n    const [nearestNode, nearestOffset] = exactMatch\n      ? domPoint\n      : normalizeDOMPoint(domPoint)","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/ianstormtaylor/slate/blob/72a37c701e5da4bb13a305d416d9c070d19d3a45/packages/slate-dom/src/plugin/dom-editor.ts#L670-L706","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check DOMEditor.hasDOMNode(editor, el) before calling toSlateNode","Use event.target.closest('[data-slate-node]') to ensure you start from a Slate-managed element","For void element UI, use the element's data attributes or the void node context rather than toSlateNode on portals","Verify you're using the correct editor instance"],"exampleFix":"// before\nconst node = DOMEditor.toSlateNode(editor, toolbarEl)\n// after\nif (DOMEditor.hasDOMNode(editor, toolbarEl)) {\n  const node = DOMEditor.toSlateNode(editor, toolbarEl)\n}","handlingStrategy":"type-guard","validationCode":"const el = (event.target as HTMLElement).closest('[data-slate-node]')\nif (el && DOMEditor.hasDOMNode(editor, el)) {\n  const node = DOMEditor.toSlateNode(editor, el)\n}","typeGuard":"const isSlateManagedEl = (editor: Editor, el: HTMLElement): boolean =>\n  DOMEditor.hasDOMNode(editor, el)","tryCatchPattern":"try { DOMEditor.toSlateNode(editor, el) } catch (e) { if (/Cannot resolve a Slate node/.test(e.message)) ignore non-Slate DOM; else throw e }","preventionTips":["Always closest('[data-slate-node]') from event targets","Guard with hasDOMNode before toSlateNode","Don't feed portal/toolbar elements to editor APIs"],"tags":["slate","dom","node-mapping"],"backgroundTag":"dom-node-outside-editor","analyzedSha":"72a37c701e5da4bb13a305d416d9c070d19d3a45","analyzedAt":"2026-08-27T23:04:51.145Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}