toeverything/AFFiNE · error · BlockSuiteError

EdgelessExportError

EdgelessExportError

Error message

Could not find edgeless block component.

What it means

Thrown during edgeless-to-canvas export by _drawTopLevelBlock when std.view.getBlock(block.id) returns null/undefined for a block model being rendered to the canvas. html2canvas needs the live DOM component; if it is not mounted/registered in the view layer the export cannot proceed. Error code EdgelessExportError.

Source

Thrown at blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts:507

        boxShadowElements.forEach(function (element) {
          if (element instanceof HTMLElement) {
            element.style.setProperty('box-shadow', 'none');
          }
        });
        await replaceImgSrcWithSvg?.(element);
      },
      backgroundColor: 'transparent',
      useCORS: imageProxy ? false : true,
      proxy: imageProxy,
    };

    const _drawTopLevelBlock = async (
      block: GfxBlockElementModel,
      isInFrame = false
    ) => {
      const blockComponent = this.std.view.getBlock(block.id);
      if (!blockComponent) {
        throw new BlockSuiteError(
          ErrorCode.EdgelessExportError,
          'Could not find edgeless block component.'
        );
      }

      const blockBound = Bound.deserialize(block.xywh);
      const canvasData = await html2canvas(
        blockComponent as HTMLElement,
        html2canvasOption
      );
      ctx.drawImage(
        canvasData,
        blockBound.x - bound.x + padding,
        blockBound.y - bound.y + padding,
        blockBound.w,
        isInFrame
          ? (blockBound.w / canvasData.width) * canvasData.height
          : blockBound.h

View on GitHub (pinned to 26c515e050)

Solutions

  1. Ensure all blocks in the export bound are mounted before exporting — scroll them into view or disable virtualisation during export.
  2. Confirm all referenced block flavours have their components registered with the editor host.
  3. Filter the node list passed to _edgelessToCanvas to blocks whose std.view.getBlock(id) is non-null, skipping unmounted ones.

Example fix

// before
// export selection includes a not-yet-mounted block -> throws

// after
// wait one frame / use the readiness check before exporting
await exportManager.docToCanvas(); // runs after _checkReady confirms mount
Defensive patterns

Strategy: validation

Validate before calling

const mounted = nodes.filter(b => !!std.view.getBlock(b.id));
// pass only mounted blocks to the export
await edgelessToCanvas(bound, mounted, canvasElements, opts);

Type guard

function isBlockMounted(std, id) {
  return !!std.view.getBlock(id);
}

Try / catch

import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
try { await edgelessToCanvas(...); }
catch (e) {
  if (e instanceof BlockSuiteError && e.code === ErrorCode.EdgelessExportError) {
    // retry after ensuring mount, or skip missing blocks
  } else throw e;
}

Prevention

When it happens

Trigger: Exporting an edgeless canvas to an image while one of the blocks in the export bound has no rendered component — e.g. it is lazy-loaded/virtualised off-screen, was just deleted, belongs to an unregistered flavour, or the view layer has not yet mounted it.

Common situations: Virtualised/lazy-rendered blocks not yet in the DOM at export time; exporting immediately after adding blocks before React mounts them; exporting a selection that includes a block whose flavour component is not registered; race after block deletion where the model lingers but the component is gone.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/ee49801bf11f281b. Report an issue: GitHub.