heygen-com/hyperframes · error · Error
drawElement: composition root has no parent node
Error message
drawElement: composition root has no parent node
What it means
Thrown during injectDrawElementCanvas() when the composition root element (matched by [data-composition-id]) has a null parentNode. The canvas injection flow needs a parent to insertBefore(canvas, root) and then reparent the root into the canvas. A detached root (not attached to document.body) has no parent and cannot be wrapped.
Source
Thrown at packages/engine/src/services/drawElementService.ts:223
height: number,
): Promise<void> {
await page.evaluate(
({ w, h }: { w: number; h: number }) => {
const root = document.querySelector("[data-composition-id]") as HTMLElement | null;
if (!root || document.getElementById("__hf_de_canvas")) return;
// Record the root's base opacity (timeline at 0, before any entrance/
// outro tween) for the LEGACY root-opacity ratio correction. The
// correction only runs on paths whose paint does not bake the root's
// current opacity into the snapshot — BeginFrame (sync=false) captures
// and builds without canvas.requestPaint(); see __hfDeInvalidate below.
try {
(window as unknown as { __HF_ROOT_BASE_OPACITY__?: number }).__HF_ROOT_BASE_OPACITY__ =
parseFloat(getComputedStyle(root).opacity) || 1;
} catch {
/* leave undefined → ratio defaults to 1 */
}
const parent = root.parentNode;
if (!parent) throw new Error("drawElement: composition root has no parent node");
const canvas = document.createElement("canvas") as HTMLCanvasElement & {
layoutsubtree: boolean;
};
canvas.id = "__hf_de_canvas";
canvas.setAttribute("layoutsubtree", "");
canvas.width = w;
canvas.height = h;
canvas.style.cssText = "display:block;position:absolute;top:0;left:0;z-index:0";
parent.insertBefore(canvas, root);
canvas.appendChild(root);
// Invalidation sentinel: a canvas child OUTSIDE the captured root.
// Toggling its background each capture is a PAINT-level dirty
// (layout/transform toggles do NOT fire the canvas `paint` event), so a
// paint — and a fresh snapshot — is guaranteed even for static frames,
// without the sentinel ever appearing in drawElementImage(root) output.
const tick = document.createElement("div");
tick.id = "__hf_de_tick";
tick.style.cssText =View on GitHub (pinned to c2996c8626)
Solutions
- Ensure injectDrawElementCanvas is called after the composition root is in the live DOM: wait for document.querySelector('[data-composition-id]') to return a node whose parentNode is non-null.
- Use page.waitForSelector('[data-composition-id]') before calling injectDrawElementCanvas.
- Verify the composition HTML template includes the root as a child of body (not a detached fragment).
- If using shadow DOM, adjust the querySelector to pierce it or attach the canvas inside the shadow root.
Example fix
// before
await page.goto(url, { waitUntil: 'domcontentloaded' });
await injectDrawElementCanvas(page, w, h);
// after
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-composition-id]');
await injectDrawElementCanvas(page, w, h); Defensive patterns
Strategy: validation
Validate before calling
// before calling injectDrawElementCanvas:
const hasRoot = await page.evaluate(() => {
const root = document.querySelector('[data-composition-id]');
return root !== null && root.parentNode !== null;
});
if (!hasRoot) {
throw new Error('Composition root not attached to DOM');
} Try / catch
try {
await injectDrawElementCanvas(page, w, h);
} catch (err) {
if (err instanceof Error && err.message.includes('no parent node')) {
// wait for DOM and retry
await page.waitForSelector('[data-composition-id]');
await injectDrawElementCanvas(page, w, h);
}
throw err;
} Prevention
- Call page.waitForSelector('[data-composition-id]') before injectDrawElementCanvas.
- Ensure composition templates attach the root to document.body.
- Avoid scripts that remove or replace the root element after load.
When it happens
Trigger: injectDrawElementCanvas(page, width, height) calls page.evaluate() which queries [data-composition-id], finds it, checks document.getElementById('__hf_de_canvas') is absent, then reads root.parentNode. If the root was created but never appended to the document (or was removed), parentNode is null.
Common situations: The composition HTML loaded but the root element was removed by a script before injection ran. injectDrawElementCanvas was called before the page finished loading the composition DOM (race condition with page.goto). A custom composition template doesn't attach the root to document.body. The root was moved to a shadow DOM.
Related errors
- drawElement canvas not initialized
- drawElement: toDataURL returned no base64 payload
- [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/5f0022b896fd6766.
Report an issue: GitHub.