Skip to content

fix(engine): name the element a failing root composition script looked up - #6430

Merged
miguel-heygen merged 4 commits into
mainfrom
fix/root-script-error-names-element
Oct 26, 2026
Merged

miguel-heygen merged 4 commits into
mainfrom
fix/root-script-error-names-element

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

What

When the root composition's script throws and its timeline never registers, the render still refuses with sub_timeline_script_failure, but the message now says where the script threw (hyperframes-composition://body:LINE:COL) and, when that line holds exactly one getElementById / querySelector* lookup, that lookup (for example getElementById("hero-title")). With several lookups on the line only the exact position is given, since the column cannot say which one returned null.

Why

Today the message is only TypeError: Cannot read properties of null (reading '_gsap'), which names no element, so the author cannot tell which lookup returned null.

Related work

Refs sub-composition isolation: unchanged, nothing here changes which failures refuse or skip.

How

describeThrowSite takes the first authored frame of the CDP exceptionDetails.stackTrace, fetches that script's source with Debugger.getScriptSource (debugger enabled only after a throw), and appends the lookup call only if the line has exactly one. Otherwise it keeps url:line:col. The error is recorded immediately so fail-fast is not delayed; the site is appended in place once the source arrives. Errors logged by a scoped-composition wrapper reach pageErrors through the console and carry no stack, so they are unchanged.

Test plan

  • Unit tests added/updated: browser test throws from gsap.set([document.getElementById("hero-title")]) in a hyperframes-composition://body script and asserts the warning names the line and the id.
  • Manual testing performed: run on a remote box, frameCapture-isolateScript.browser.test.ts plus frameCapture.test.ts, 3 runs in a row, 2 files and 64 of 64 tests each. Breaking describeThrowSite (returning an empty site) fails both new tests (expected 'A composition script threw before its…' to contain 'hyperframes-composition://body:2:', 2 failed | 5 passed); on main today the function does not exist, so the message carries no position.
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments"

Why under 100 lines

This is one diagnostic for a failure class still live in production renders (a root script that throws on a missing node, with a message that names no element), and nothing else in frameCapture.ts is open that could carry it. Splitting the throw-site lookup from its test would leave each half unreviewable on its own.

@github-actions

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 189 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 blocker · 0 important · 0 nits · head 418a3157

Scope
  • Re-pinned from 119b07df to 418a3157. The new commit names a lookup only when it is the only one on the throwing line. That fixes the case where the call site sits before every lookup on its line and the old fallback named the first lookup, which could be the wrong element.
  • The site is appended in place after recordPageError and the includes check removes the duplicate entry. pageErrors is only read after the grace window. No other code uses the Debugger domain, so Debugger.disable clashes with nothing.
  • Cost: a script that throws every frame now enables and disables the debugger once per throw. Measured at about 1–2 ms per call, fire-and-forget, so it does not block capture.
  • Run locally: frameCapture* tests pass (329, including the browser isolate-script suite on Chromium). CI on this head is all green.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 83a29e9 Oct 26, 2026
122 of 123 checks passed
@miguel-heygen
miguel-heygen deleted the fix/root-script-error-names-element branch October 26, 2026 17:12
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Oct 26, 2026
Merge 2 more upstream commits (7d6c02f..acd0bc3: heygen-com#5366, heygen-com#3943).
Regenerate skills-manifest.json for the media-use hash conflict. Record
the 10 open PRs heygen-com#6430-heygen-com#6439 in docs/DECISIONS.md (none adopted; heygen-com#6436
cannot be copied because the fork wraps authored scripts in Function())
and advance the commit watermark to acd0bc3 and the PR watermark to
heygen-com#6439.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Oct 26, 2026
Merge 20 more upstream commits (acd0bc3..ad4d3fa, v0.8.205: heygen-com#6403,
heygen-com#6363, heygen-com#6209, heygen-com#6395, heygen-com#6424, heygen-com#6426, heygen-com#6442, heygen-com#6430, heygen-com#6402, heygen-com#6435, heygen-com#6439,
heygen-com#6449, heygen-com#5436, heygen-com#6455 and docs). No conflicts. Record the 11 open PRs
heygen-com#6440-heygen-com#6458 and issue heygen-com#6443 in docs/DECISIONS.md (none adopted), re-check
the earlier open PRs, and advance the commit watermark to ad4d3fa, the
PR watermark to heygen-com#6458 and the issue watermark to heygen-com#6443.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant