toeverything/AFFiNE · warning · Error

Unable to export content to canvas

Error message

Unable to export content to canvas

What it means

Thrown by EdgelessClipboardController._checkCanContinueToCanvas when, partway through an async canvas export, location.pathname no longer equals the pathName captured at the start, OR isInsidePageEditor(host) has flipped from the captured editorMode. It is a guard that aborts the expensive html2canvas pipeline if the user navigated away or switched between page/edgeless modes during the export. A plain Error.

Source

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

  private get crud() {
    return this.std.get(EdgelessCRUDIdentifier);
  }

  private get toolManager() {
    return this.gfx.tool;
  }

  private _checkCanContinueToCanvas(
    host: EditorHost,
    pathName: string,
    editorMode: boolean
  ) {
    if (
      location.pathname !== pathName ||
      isInsidePageEditor(host) !== editorMode
    ) {
      throw new Error('Unable to export content to canvas');
    }
  }

  private async _edgelessToCanvas(
    bound: IBound,
    nodes?: GfxBlockElementModel[],
    canvasElements: GfxPrimitiveElementModel[] = [],
    {
      background,
      padding = IMAGE_PADDING,
      dpr = window.devicePixelRatio || 1,
    }: CanvasExportOptions = {}
  ): Promise<HTMLCanvasElement | undefined> {
    const host = this.std.host;
    const rootModel = this.doc.root;
    if (!rootModel) return;

    const html2canvas = (await import('html2canvas')).default;

View on GitHub (pinned to 26c515e050)

Solutions

  1. Do not navigate or switch editor modes during an export; await the export promise before allowing route changes.
  2. Wrap the export call in try/catch and treat this error as a cancellation (not a fatal failure) — show a 'cancelled' message to the user.
  3. If exporting programmatically, lock navigation / disable the mode toggle until the promise resolves.

Example fix

// before
try { await clipboardToCanvas(...); } catch (e) { /* unhandled */ }

// after
try {
  await clipboardToCanvas(...);
} catch (e) {
  if (e instanceof Error && /export content to canvas/.test(e.message)) {
    notify('Export cancelled: the view changed.');
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// capture navigation state and lock UI during export
const pathName = location.pathname;
const editorMode = isInsidePageEditor(host);
// disable navigation/mode toggle while exporting
await exportPromise;

Try / catch

try { await clipboardToCanvas(...); }
catch (e) {
  if (e instanceof Error && /export content to canvas/.test(e.message)) {
    // treat as cancellation, not a fault
  } else throw e;
}

Prevention

When it happens

Trigger: Triggering an edgeless-to-canvas export (copy as image / export) and then navigating to another doc or toggling page-vs-edgeless mode before the async html2canvas pipeline completes. The check is re-evaluated at continuation points in _edgelessToCanvas.

Common situations: Slow exports on large canvases where the user clicks away; SPAs where route changes happen during export; programmatic exports invoked while other code mutates the route or mode.

Related errors


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