{"record":{"id":"0ae510e0abfff33c","repo":"heygen-com/hyperframes","slug":"could-not-determine-composition-duration-no-layo","errorCode":null,"errorMessage":"Could not determine composition duration — no layout samples run","messagePattern":"Could not determine composition duration — no layout samples run","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/checkPipeline.ts","lineNumber":153,"sourceCode":"    at: options.at,\n  });\n  const transitions = options.atTransitions\n    ? buildTransitionSampleTimes({\n        duration,\n        boundaries: await driver.getTransitionBoundaries(),\n        cap: options.maxTransitionSamples,\n      })\n    : { times: [], dropped: 0 };\n  const captionSamples = options.captionZone\n    ? gateSampleTimes(duration, options.captionZone.seek, 1)\n    : [];\n  const frameSamples = options.frameCheck\n    ? gateSampleTimes(duration, options.frameCheck.seek, 0.5)\n    : [];\n  const auditSamples = mergeSampleTimes(baseSamples, transitions.times);\n  const layoutSamples = mergeSampleTimes(auditSamples, captionSamples, frameSamples);\n  if (layoutSamples.length === 0) {\n    throw new Error(\"Could not determine composition duration — no layout samples run\");\n  }\n  return {\n    duration,\n    layoutSamples,\n    captionSamples,\n    frameSamples,\n    transitionSamples: transitions.times,\n    transitionSamplesDropped: transitions.dropped,\n    contrastSamples: options.contrast ? selectContrastTimes(auditSamples) : [],\n  };\n}\n\ninterface MotionPlan {\n  times: number[];\n  selectors: string[];\n  livenessScopes: string[];\n  preflightIssues: AnchoredLayoutIssue[];\n}","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/utils/checkPipeline.ts#L135-L171","documentation":"Thrown inside buildSampleGrid when layoutSamples (the merge of base layout samples, transition times, caption samples, and frame samples) is empty. buildLayoutSampleTimes and gateSampleTimes both return [] when duration is not finite or <= 0, so the practical root cause is that driver.getDuration() returned 0, NaN, Infinity, or a negative number. Without sample times the audit grid cannot run at all.","triggerScenarios":"The browser driver reported a composition duration of 0, NaN, or negative — typically because the GSAP timeline has no duration attribute, the composition's data-duration is unset/zero, or the page failed to expose a seekable timeline so getDuration fell back to 0. Also reachable if options.samples is 0 and no transition/caption/frame samples fill the grid.","commonSituations":"A composition that autoplays a CSS animation with no data-duration (the runtime cannot measure total duration); a draft index.html with no clip/timeline; a mock driver in tests returning 0; a page where the timeline throws during initialization so getDuration returns a sentinel.","solutions":["Ensure the composition declares a duration: set data-duration on the composition root or register a paused GSAP timeline whose totalDuration() is positive.","Open the composition in a browser and confirm window.__hyperframes.getDuration() returns a positive finite number.","If using a custom CheckAuditDriver, verify getDuration() resolves to a positive finite value before runAuditGrid.","Check that options.samples is >= 1 (DEFAULT_CHECK_OPTIONS.samples is 9)."],"exampleFix":"<!-- before: no duration, getDuration returns 0 -->\n<div data-composition-id=\"root\">\n  <!-- clips with no aggregate duration -->\n</div>\n<!-- after -->\n<div data-composition-id=\"root\" data-duration=\"10\">\n  <div class=\"clip\" data-start=\"0\" data-end=\"10\">...</div>\n</div>","handlingStrategy":"validation","validationCode":"const duration = await driver.getDuration();\nif (!Number.isFinite(duration) || duration <= 0) {\n  throw new Error(`Composition duration is invalid (${duration}); set data-duration on the root or register a paused GSAP timeline.`);\n}\n// safe to call runAuditGrid now","typeGuard":"function hasPositiveFiniteDuration(d: unknown): d is number {\n  return typeof d === 'number' && Number.isFinite(d) && d > 0;\n}","tryCatchPattern":"try {\n  return await runAuditGrid(driver, options, motion);\n} catch (err) {\n  if (err instanceof Error && /no layout samples run/.test(err.message)) {\n    // duration was 0/NaN — fix the composition's data-duration/timeline, then retry\n    console.error('Audit aborted: composition reported no duration. Add data-duration to the root.');\n    return emptyBrowserResult();\n  }\n  throw err;\n}","preventionTips":["Always set data-duration on the composition root, or register a paused GSAP timeline with a real totalDuration().","In tests with a mock driver, have getDuration() return a positive finite value.","Run `hyperframes lint` before `check` — a missing duration surfaces early."],"tags":["check-pipeline","duration","layout-audit","composition","gsap"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}