toeverything/AFFiNE · warning · BlockSuiteError

EdgelessExportError

EdgelessExportError

Error message

Unable to export content to canvas

What it means

Thrown by ExportManager._checkCanContinueToCanvas (the surface block's export manager) under the same conditions as the clipboard variant [11]: location.pathname changed or isInsidePageEditor(editorHost) flipped between the start of the export and a continuation check. Error code EdgelessExportError (a structured BlockSuiteError, unlike [11]'s plain Error). It is polled inside _checkReady's interval and rejects the readiness promise on change.

Source

Thrown at blocksuite/affine/blocks/surface/src/extensions/export-manager/export-manager.ts:112

    await Promise.all(promises);
  };

  get doc(): Store {
    return this.std.store;
  }

  get editorHost(): EditorHost {
    return this.std.host;
  }

  constructor(readonly std: BlockStdScope) {}

  private _checkCanContinueToCanvas(pathName: string, editorMode: boolean) {
    if (
      location.pathname !== pathName ||
      isInsidePageEditor(this.editorHost) !== editorMode
    ) {
      throw new BlockSuiteError(
        ErrorCode.EdgelessExportError,
        'Unable to export content to canvas'
      );
    }
  }

  private async _checkReady() {
    const pathname = location.pathname;
    const editorMode = isInsidePageEditor(this.editorHost);

    const promise = new Promise((resolve, reject) => {
      let count = 0;
      const checkReactRender = setInterval(() => {
        try {
          this._checkCanContinueToCanvas(pathname, editorMode);
        } catch (e) {
          clearInterval(checkReactRender);
          reject(e);

View on GitHub (pinned to 26c515e050)

Solutions

  1. Await the export promise and prevent route/mode changes until it resolves (disable the toggle, lock navigation).
  2. Catch BlockSuiteError with code EdgelessExportError at the call site and treat it as cancellation, not a fatal fault.
  3. Reduce export bound / pre-render off-screen blocks to shorten the window in which navigation can invalidate the export.

Example fix

// before
await exportManager.edgelessToCanvas(bound); // throws if user navigates

// after
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
try {
  await exportManager.edgelessToCanvas(bound);
} catch (e) {
  if (e instanceof BlockSuiteError && e.code === ErrorCode.EdgelessExportError) {
    notify('Export cancelled: view changed.');
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// prevent navigation/mode changes during export
const pathName = location.pathname;
const editorMode = isInsidePageEditor(editorHost);
setNavigationLocked(true);
await exportManager.edgelessToCanvas(bound);
setNavigationLocked(false);

Try / catch

import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
try { await exportManager.edgelessToCanvas(bound); }
catch (e) {
  if (e instanceof BlockSuiteError && e.code === ErrorCode.EdgelessExportError) {
    // cancellation: view changed mid-export
  } else throw e;
}

Prevention

When it happens

Trigger: Calling ExportManager.docToCanvas/edgelessToCanvas and navigating to a different route or toggling page/edgeless mode while the readiness interval (10s window, 100ms ticks) or the html2canvas pipeline is still running. Also triggered if the SPA route changes for any reason mid-export.

Common situations: Long exports on heavy docs where the user navigates away; automated exports run concurrently with other code that mutates the route; browser back/forward during export; mode-toggle shortcuts pressed during export.

Related errors


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