toeverything/AFFiNE · error · BlockSuiteError
ValueNotExists
ValueNotExists
Error message
Failed to create portal root
What it means
createSimplePortal() attaches a shadow root to a wrapper div (when shadowDom is truthy) then reads portalRoot.shadowRoot. If that read returns null the function throws ValueNotExists. attachShadow can silently fail to populate shadowRoot on hosts that already have a shadow root, in environments lacking Shadow DOM support, or when the element is an invalid shadow host.
Source
Thrown at blocksuite/affine/components/src/portal/helper.ts:44
identifyWrapper = true,
}: PortalOptions) {
const portalRoot = document.createElement('div');
if (identifyWrapper) {
portalRoot.classList.add('blocksuite-portal');
}
if (shadowDom) {
portalRoot.attachShadow({
mode: 'open',
...(typeof shadowDom !== 'boolean' ? shadowDom : {}),
});
}
signal.addEventListener('abort', () => {
portalRoot.remove();
});
const root = shadowDom ? portalRoot.shadowRoot : portalRoot;
if (!root) {
throw new BlockSuiteError(
BlockSuiteError.ErrorCode.ValueNotExists,
'Failed to create portal root'
);
}
let updateId = 0;
const updatePortal: (id: number) => void = id => {
if (id !== updateId) {
console.warn(
'Potentially infinite recursion! Please clean up the old event listeners before `updatePortal`'
);
return;
}
updateId++;
const curId = updateId;
const templateResult =
template instanceof Function
? template({ updatePortal: () => updatePortal(curId) })View on GitHub (pinned to 26c515e050)
Solutions
- Ensure the runtime provides Shadow DOM (use a polyfill in jsdom/SSR tests).
- Pass shadowDom=false to render into the light DOM instead.
- Confirm createSimplePortal is called in a browser context after DOM is ready.
Example fix
// before
createSimplePortal({ template, shadowDom: true }); // fails in jsdom
// after
createSimplePortal({ template, shadowDom: false }); Defensive patterns
Strategy: validation
Validate before calling
const supportsShadow = typeof Element !== 'undefined' && typeof Element.prototype.attachShadow === 'function';
createSimplePortal({ template, shadowDom: supportsShadow }); Type guard
const canHostShadow = (el: Element): boolean => typeof el.attachShadow === 'function';
Prevention
- Detect Shadow DOM support before enabling shadowDom option
- Use shadowDom:false in jsdom/SSR tests
When it happens
Trigger: Calling createSimplePortal with shadowDom=true (or an options object) in an environment where Shadow DOM is unavailable, or passing a custom element/tag that cannot host a shadow root.
Common situations: Running under jsdom/test environments without Shadow DOM polyfills; SSR where document APIs are stubbed; re-attaching a shadow root to an element that already has one.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/b8799bfa46e660ed.
Report an issue: GitHub.