heygen-com/hyperframes · error · Error

drawElement: toDataURL returned no base64 payload

Error message

drawElement: toDataURL returned no base64 payload

What it means

Thrown when canvas.toDataURL() returns a string with no comma-separated base64 payload. A valid data URL looks like 'data:image/png;base64,<data>'; splitting on ',' yields [mime, base64]. If the string is empty, 'data:', or has no comma, index 1 is undefined and the error fires. This typically indicates a tainted canvas (cross-origin content) or an empty/corrupted canvas.

Source

Thrown at packages/engine/src/services/drawElementService.ts:542

        // seek produced no paint-level change (static scene, or transform-only
        // GSAP updates that are compositor-side and never repaint). Sentinel
        // dirty + requestPaint(), installed by injectDrawElementCanvas — see
        // __hfDeInvalidate there for the full mechanism/rationale.
        usedRequestPaint = aw.__hfDeInvalidate?.() === true;
        // Safety net: if the paint event doesn't arrive (feature drift /
        // throttled page), fall back to an unsynchronized draw after 250 ms —
        // worst case one-frame-stale content (the root's alpha may lag its
        // transform by that frame) rather than a hung render.
        setTimeout(() => {
          canvas.removeEventListener("paint", onPaint);
          drawAndEncode();
        }, 250);
      });
    },
    { w: width, h: height, fmt: format, q: quality, sync: syncToPaintEvent },
  );
  const base64 = dataUrl.split(",")[1];
  if (!base64) throw new Error("drawElement: toDataURL returned no base64 payload");
  return Buffer.from(base64, "base64");
}

// ── Worker-encode pipeline ────────────────────────────────────────────────────
//
// Architecture: an in-page OffscreenCanvas Worker encodes JPEG frames off the
// main thread. The main thread does seek+paint+drawElement+createImageBitmap
// (the "produce" phase) and immediately transfers the bitmap to the worker.
// The worker encodes it concurrently while the main thread processes the next
// frame — hiding ~7.4ms of encode cost behind ~8.4ms of produce work.
//
// The worker posts the encoded bytes back by calling window.__hfFrameReady
// (a Puppeteer exposeFunction binding that calls a node-side callback).
// Node resolves the per-frame Promise from that callback.

interface WorkerEncodeEntry {
  resolve: (buf: Buffer) => void;
  reject: (err: Error) => void;

View on GitHub (pinned to c2996c8626)

Solutions

  1. Ensure all images drawn to the canvas have crossorigin='anonymous' and proper CORS headers.
  2. Verify canvas.width and canvas.height are positive before toDataURL.
  3. Check the dataUrl string in the error path: log it to distinguish '' (tainted) from 'data:' (empty).
  4. If using file:// origin (as the FX host does), ensure assets are local to avoid cross-origin taint.
Defensive patterns

Strategy: validation

Validate before calling

// verify canvas is not tainted before capture
const notTainted = await page.evaluate(() => {
  const canvas = document.getElementById('__hf_de_canvas') as HTMLCanvasElement;
  if (!canvas) return false;
  try {
    const url = canvas.toDataURL('image/png');
    return url.includes(',') && url.split(',')[1].length > 0;
  } catch {
    return false; // SecurityError = tainted
  }
});
if (!notTainted) {
  throw new Error('drawElement canvas is tainted or empty');
}

Try / catch

try {
  const buf = await captureDrawElement(page, opts);
} catch (err) {
  if (err instanceof Error && err.message.includes('toDataURL returned no base64')) {
    // check for cross-origin images, CORS headers, tainted canvas
  }
  throw err;
}

Prevention

When it happens

Trigger: The drawElement capture evaluate calls canvas.toDataURL('image/png') (or jpeg), returns the dataUrl string to Node, and the code does dataUrl.split(',')[1]. If toDataURL returned '' or 'data:' (which happens for a tainted or zero-size canvas), base64 is undefined.

Common situations: The canvas drew a cross-origin image without CORS headers, tainting it — toDataURL returns 'data:' with no payload. The canvas dimensions are zero. The canvas was cleared or never drawn to. A browser security policy blocks data URL extraction.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/2edd7818d5d50deb. Report an issue: GitHub.