{"record":{"id":"cdc16e3d833c21d2","repo":"heygen-com/hyperframes","slug":"shot-nothing-animates-under-scopeselector","errorCode":null,"errorMessage":"--shot: nothing animates under '${scopeSelector}'. Nearest animated elements: ${nearest || \"(none)\"}.","messagePattern":"--shot: nothing animates under '(.+?)'\\. Nearest animated elements: (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/cli/src/commands/motionShot.ts","lineNumber":452,"sourceCode":"async function resolveScopedRequests(\n  page: import(\"puppeteer-core\").Page,\n  requests: ShotRequest[],\n  scopeSelector: string,\n): Promise<ShotRequest[]> {\n  const resolved = await resolveScopeInBrowser(\n    page,\n    scopeSelector,\n    requests.map((r) => r.selector),\n  );\n  if (!resolved.scopeExists) {\n    throw new Error(`--shot: --selector '${scopeSelector}' matched no element.`);\n  }\n  if (resolved.selectors.length === 0) {\n    const nearest = requests\n      .slice(0, 5)\n      .map((r) => r.selector)\n      .join(\", \");\n    throw new Error(\n      `--shot: nothing animates under '${scopeSelector}'. Nearest animated elements: ${nearest || \"(none)\"}.`,\n    );\n  }\n  return resolved.selectors.map((selector) => ({ selector }));\n}\n\n// In-tick capture: seek the timeline (fires the composition's onUpdate render\n// synchronously via the shared window.__hfSeekAllAdapters) + nudge the\n// three-adapter, then drawImage every <canvas> onto an offscreen canvas in the\n// SAME tick — before the browser clears the GL drawing buffer (works without\n// preserveDrawingBuffer; page.screenshot can't see the GL buffer here).\nfunction captureGhostFrame(page: import(\"puppeteer-core\").Page, t: number): Promise<string> {\n  return page.evaluate(async (tt: number) => {\n    await (\n      window as unknown as { __hfSeekAllAdapters?: (time: number) => Promise<void> }\n    ).__hfSeekAllAdapters?.(tt);\n    const root = (document.querySelector(\"[data-composition-id]\") ?? document.body) as HTMLElement;\n    const rb = root.getBoundingClientRect();","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/commands/motionShot.ts#L434-L470","documentation":"Thrown by resolveScopedRequests when the --selector scope matched a real element (scopeExists === true) but no animated selectors were found underneath it — neither the scope element itself nor any descendant is in the animated-requests list. The message lists the nearest animated elements (up to 5) as diagnostic context so you can re-scope correctly.","triggerScenarios":"Scoping to a static wrapper element (e.g. a .clip container) that has no animated children, or whose animated children are outside the request set. The scope exists but carries no motion sampled by the shot.","commonSituations":"Targeting a layout container instead of the animated child. Compositions where animation is on a sibling rather than a descendant. Selectors that match a decorative wrapper with no tweens.","solutions":["Re-scope to one of the nearest animated elements listed in the error message","Drop --selector to let the shot sample all detected animated elements","Verify the element you expect to animate actually has a registered timeline (window.__timelines)","Ensure GSAP timelines are registered on window.__timelines so they are detected as animated"],"exampleFix":"// before — static wrapper has no animated children\nhyperframes shot --selector '.static-clip' out.png\n// after — scope the element that actually animates\nhyperframes shot --selector '.animated-child' out.png","handlingStrategy":"validation","validationCode":"// After bundling, check the scope has animated descendants:\nconst animated = await page.evaluate((scope: string) => {\n  const root = document.querySelector(scope);\n  if (!root) return { exists: false, animated: 0 };\n  const timelines = Object.keys((window as any).__timelines ?? {});\n  const animated = timelines.filter(sel => {\n    const el = document.querySelector(sel);\n    return el && (el === root || root.contains(el));\n  }).length;\n  return { exists: true, animated };\n}, scopeSelector);\nif (animated.animated === 0) {\n  // pick a different scope or drop --selector\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Scope to elements known to carry registered timelines (window.__timelines)","If unsure, omit --selector to sample all animated elements"],"tags":["motion-shot","selector","scope","no-motion","cli"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}