toeverything/AFFiNE · error · BlockSuiteError
ErrorCode.InlineEditorError
ErrorCode.InlineEditorError
Error message
failed to find vElement for a text note in an embed element
What it means
Thrown by `inlineRangeToDomRange(rootElement, inlineRange)` in range-conversion.ts while translating an editor `InlineRange` into a native DOM `Range`. The resolved start (anchor) `Text` node was detected inside an embed (`isInEmbedElement` returned true via the `data-v-embed="true"` marker), but `parentElement?.closest('v-element')` returned null. BlockSuite's inline DOM invariant requires every embed text node to live inside a `<v-element>` custom element; a missing wrapper means the rendered tree is inconsistent with the delta model.
Source
Thrown at blocksuite/framework/std/src/inline/utils/range-conversion.ts:307
if (startText && endText) {
break;
}
index += textLength;
}
// the one because of the line break
index += 1;
}
if (!startText || !endText) {
return null;
}
if (isInEmbedElement(startText)) {
const anchorVElement = startText.parentElement?.closest('v-element');
if (!anchorVElement) {
throw new BlockSuiteError(
ErrorCode.InlineEditorError,
'failed to find vElement for a text note in an embed element'
);
}
const nextSibling = anchorVElement.nextElementSibling;
if (!nextSibling) {
throw new BlockSuiteError(
ErrorCode.InlineEditorError,
'failed to find nextSibling sibling of an embed element'
);
}
const texts = getTextNodesFromElement(nextSibling);
if (texts.length === 0) {
throw new BlockSuiteError(
ErrorCode.InlineEditorError,
'text node in v-text not found'
);View on GitHub (pinned to 26c515e050)
Solutions
- Render embed nodes through BlockSuite's `VElement` (`v-element`) wrappers rather than bare spans.
- Before triggering range conversion, log `rootElement.querySelectorAll('[data-v-embed="true"]')` and verify each has an ancestor `v-element`.
- Gate range/sync calls on `editor.mounted` and the host's `hasUpdated`; skip them during teardown.
- If you ship a custom inline render hook, audit it against `@blocksuite/std` `inline-v-element` / `inline-embed` components.
Example fix
// before: embed text rendered without the v-element wrapper
html`<span data-v-embed="true">${node.text}</span>`
// after: wrap embed content in a VElement so the invariant holds
html`<v-element><span data-v-embed="true">${node.text}</span></v-element>` Defensive patterns
Strategy: validation
Validate before calling
// run before inlineRangeToDomRange to confirm the embed DOM invariant
function isEmbedDomIntact(root: HTMLElement): boolean {
const embeds = Array.from(root.querySelectorAll('[data-v-embed="true"]'));
return embeds.every(el => !!el.closest('v-element'));
}
if (!isEmbedDomIntact(rootEl)) {
// skip range conversion instead of throwing
return null;
} Type guard
function hasVElementWrapper(node: Text): boolean {
return !!node.parentElement?.closest('v-element');
} Try / catch
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
try {
return inlineRangeToDomRange(root, range);
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.InlineEditorError) {
// DOM invariant broken: degrade gracefully rather than crash selection sync
console.warn('inlineRangeToDomRange skipped: embed DOM invariant violated', e);
return null;
}
throw e;
} Prevention
- Render embed nodes exclusively through BlockSuite's VElement helpers.
- Never hand-edit the inline DOM; mutate deltas through the editor API.
- Skip range sync while the editor is unmounting.
- Gate range conversion on `editor.mounted` and `host.hasUpdated`.
When it happens
Trigger: Calling `InlineEditor`/`RangeManager` APIs that invoke `inlineRangeToDomRange` (e.g. selection sync, `toDomRange`, programmatic range restore) when a custom inline embed renderer produced text marked `data-v-embed="true"` without a wrapping `<v-element>`, or while the inline DOM is being concurrently mutated/torn down so the wrapper has been removed but the embed span remains.
Common situations: Forked or custom embed attributes components that render a plain `<span data-v-embed="true">` instead of going through `VElement`; SSR/hydration mismatch; selection being restored during `disconnectedCallback`; an embed delta inserted before its component was registered with the inline renderer.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/dcbb1defdd4ea93b.
Report an issue: GitHub.