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

  1. Ensure the runtime provides Shadow DOM (use a polyfill in jsdom/SSR tests).
  2. Pass shadowDom=false to render into the light DOM instead.
  3. 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

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.