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
- Ensure all images drawn to the canvas have crossorigin='anonymous' and proper CORS headers.
- Verify canvas.width and canvas.height are positive before toDataURL.
- Check the dataUrl string in the error path: log it to distinguish '' (tainted) from 'data:' (empty).
- 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
- Set crossorigin='anonymous' on all images loaded into the composition.
- Ensure image servers return Access-Control-Allow-Origin headers.
- Use file:// or same-origin assets to avoid canvas taint.
- Verify canvas dimensions are positive before capture.
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
- drawElement: composition root has no parent node
- drawElement canvas not initialized
- [chromium] Chrome binary unavailable (source=${source}): @sp
- [chromium] Chrome binary unavailable (source=${source}): HYP
- [chromium] Chrome binary unavailable (source=${source}): HYP
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/2edd7818d5d50deb.
Report an issue: GitHub.