Skip to content

fix: stop flames crashing on the way out (teardown removeChild guard) - #33

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/fix-flame-teardown-removechild-crash
Draft

fix: stop flames crashing on the way out (teardown removeChild guard)#33
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/fix-flame-teardown-removechild-crash

Conversation

@posthog

@posthog posthog Bot commented Jul 3, 2026

Copy link
Copy Markdown

patch notes from a hedgehog who died mid-fade

One user watched a flame quietly try to removeChild itself into the void — right after the whole overlay had been torn down. Pixi had already nulled app.stage, so the flame's dying wish became Cannot read properties of null (reading 'removeChild'). Three times, in one 170ms burst, because several queued flames all flushed at once. Very dramatic. Very avoidable.

what was actually happening

removeElement unconditionally did this.app.stage.removeChild(element.sprite). Fine while the app is alive. Less fine after destroy() runs app.destroy() and Pixi sets stage to null.

The flames get there via a gsap fade whose onComplete calls removeElement. And gsap is a global singleton we drive by hand from the Matter afterUpdate handler — so nothing was cancelling those tweens on teardown. A pending fade would happily flush against a corpse.

what changed

  • removeElement now bails if app/app.stage is gone instead of dereferencing null.
  • destroy() kills any in-flight tweens (flame/ghost/web fades, dying hedgehogs) before app.destroy(), so no stray onComplete lands after the lights go out.
  • FlameActor.beforeUnload() kills its own fade tween if it's yanked early.
  • Hedgehog.beforeUnload() got the same app.stage?.removeChild guard for its accessory sprites — same latent footgun, one line away.

why

Real, reproducible production error (small blast radius — 1 user, 1 session), and mostly worth doing to stop the error-tracking channel from getting these teardown-race pings. It's a robustness fix: teardown should be able to happen mid-animation without a hedgehog throwing on its way out the door.

how it was tested

  • tsc --noEmit — clean (the dts build agrees).
  • vite build — builds, declarations and all.
  • eslint on the touched files — clean.

No new unit test: the existing test/ harness is a leftover stub (imports a helloWorld that doesn't exist and already fails), and there's no jsdom/Pixi/Matter/gsap scaffolding to meaningfully exercise a teardown race without building a mocking apparatus from scratch. The change is a set of narrow null-guards + tween cleanup, verified by the type-checker and build.


Created with PostHog Code from an inbox report.

A pending flame fade could flush its onComplete after the overlay was
destroyed, calling app.stage.removeChild on a null stage and throwing
"Cannot read properties of null (reading 'removeChild')".

- Guard removeElement so it bails when app/app.stage is gone.
- Kill in-flight gsap tweens in destroy() before app.destroy(), since
  gsap is a global singleton driven by our afterUpdate handler and
  nothing else stops them.
- Kill the fade tween in FlameActor.beforeUnload().
- Guard the same unchecked app.stage.removeChild in Hedgehog.beforeUnload.

Generated-By: PostHog Code
Task-Id: 5b49d7e2-f1aa-49a4-b327-bc5e25485d6c
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hedgehog-mode Ready Ready Preview Jul 3, 2026 9:19am
hogwars Error Error Jul 3, 2026 9:19am

Request Review

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.

0 participants