{"record":{"id":"b10de3964d659e73","repo":"ianstormtaylor/slate","slug":"cannot-resolve-a-dom-node-from-slate-node-scrub","errorCode":null,"errorMessage":"Cannot resolve a DOM node from Slate node: ${Scrubber.stringify(node)}","messagePattern":"Cannot resolve a DOM node from Slate node: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/slate-dom/src/plugin/dom-editor.ts","lineNumber":568,"sourceCode":"    if (!DOMEditor.hasTarget(editor, target)) return false\n\n    const slateNode = DOMEditor.toSlateNode(editor, target)\n    return Node.isElement(slateNode) && Editor.isVoid(editor, slateNode)\n  },\n\n  setFragmentData: (editor, data, originEvent) =>\n    editor.setFragmentData(data, originEvent),\n\n  toDOMNode: (editor, node) => {\n    const domNode =\n      node === editor\n        ? EDITOR_TO_ELEMENT.get(editor)\n        : EDITOR_TO_KEY_TO_ELEMENT.get(editor)?.get(\n            DOMEditor.findKey(editor, node)\n          )\n\n    if (!domNode) {\n      throw new Error(\n        `Cannot resolve a DOM node from Slate node: ${Scrubber.stringify(node)}`\n      )\n    }\n\n    return domNode\n  },\n\n  toDOMPoint: (editor, point) => {\n    const [node] = Editor.node(editor, point.path)\n    const el = DOMEditor.toDOMNode(editor, node)\n    let domPoint: DOMPoint | undefined\n\n    // If we're inside a void node, force the offset to 0, otherwise the zero\n    // width spacing character will result in an incorrect offset of 1\n    if (Editor.void(editor, { at: point })) {\n      point = { path: point.path, offset: 0 }\n    }\n","sourceCodeStart":550,"sourceCodeEnd":586,"githubUrl":"https://github.com/ianstormtaylor/slate/blob/72a37c701e5da4bb13a305d416d9c070d19d3a45/packages/slate-dom/src/plugin/dom-editor.ts#L550-L586","documentation":"Thrown by DOMEditor.toDOMNode when no rendered DOM element exists for the given Slate node. The node-to-element map has no entry, meaning the node is not currently rendered (or not rendered by this editor).","triggerScenarios":"Calling toDOMNode for a node that was deleted, not yet rendered (before React commits), rendered virtually (e.g. outside a windowed/virtualized list that Slate renders lazily), or belonging to another editor instance.","commonSituations":"Calling toDOMNode immediately after inserting a node in the same tick (before render), using virtualization where off-screen nodes have no DOM, or holding a stale node after normalization replaced it.","solutions":["Defer the DOM lookup to after render (requestAnimationFrame/setTimeout or useEffect)","Re-resolve the node by path/key after edits instead of reusing old node objects","If virtualizing, look up only nodes known to be in the viewport","Check the node still exists: Node.has(editor, path) before calling"],"exampleFix":"// before\nTransforms.insertNodes(editor, { text: 'hi' })\nconst el = DOMEditor.toDOMNode(editor, editor.children[0]) // too early\n// after\nrequestAnimationFrame(() => {\n  const el = DOMEditor.toDOMNode(editor, editor.children[0])\n})","handlingStrategy":"validation","validationCode":"// after render commit:\nrequestAnimationFrame(() => {\n  try { const el = DOMEditor.toDOMNode(editor, node) } catch { /* not rendered */ }\n})","typeGuard":"null","tryCatchPattern":"try { el = DOMEditor.toDOMNode(editor, node) } catch (e) { if (/Cannot resolve a DOM node/.test(e.message)) skip or re-resolve; else throw e }","preventionTips":["Defer toDOMNode until after React commits (useEffect/rAF)","Don't virtualize away nodes you need DOM access to","Re-resolve nodes by path after edits"],"tags":["slate","dom","render-timing","virtualization"],"backgroundTag":"node-not-rendered","analyzedSha":"72a37c701e5da4bb13a305d416d9c070d19d3a45","analyzedAt":"2026-08-27T23:04:51.145Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}