docs/solutions/logic-errors/2026-04-08-slate-v2-pathref-must-invalidate-on-replace.md
pathRef was implemented on top of runtime IDs, which works well for moves and
sibling shifts. But explicit snapshot replacement reuses path-based IDs, so the
ref could silently point at unrelated replacement content.
pathRef created for an old node still returned [0] after
Editor.replace(...)runtimeId -> current pathTrack a replace epoch in core and bind each pathRef to the epoch it was
created in:
const replaceEpoch = getCurrentReplaceEpoch(editor)
get current() {
return detached || getCurrentReplaceEpoch(editor) !== replaceEpoch
? null
: cloneNullablePath(getCurrentPathForRuntimeId(editor, runtimeId))
}
Increment the epoch only when an explicit snapshot replacement survives the transaction and publishes.
Moves and sibling shifts preserve logical node identity, so runtime IDs are the right anchor there. Explicit replace is a different boundary: the old document identity is gone. The replace epoch stops runtime-id reuse from masquerading as stable ref identity.
replace as an identity reset, not just another tree mutationmove, remove, and replace, not just one of
them