Skip to content

Fix SOG parsers throwing when a load completes after app teardown - #9232

Merged
mvaligursky merged 1 commit into
mainfrom
mv-sog-abort-teardown
Aug 27, 2026
Merged

Fix SOG parsers throwing when a load completes after app teardown#9232
mvaligursky merged 1 commit into
mainfrom
mv-sog-abort-teardown

Conversation

@mvaligursky

Copy link
Copy Markdown
Contributor

Loading a streamed SOG scene and destroying the app while requests are still in flight throws out of the parser instead of aborting the load cleanly:

Uncaught TypeError: Cannot read properties of null (reading 'get')
    at SogParser._shouldAbort (sog.js:116)
    at Object.callback (sog.js:295)
    at Http._onSuccess (http.js:615)

Nothing cancels an in-flight request — ResourceLoader#destroy only drops its bookkeeping and Http has no abort — so any of these callbacks can run after AppBase#destroy. Teardown happens in a fixed order: the asset registry is dropped (this.assets = null) before the graphics device is marked destroyed. That leaves a window where app.assets is null while app.graphicsDevice._destroyed is still false.

_shouldAbort dereferenced the registry in its first condition, before reaching the device check that is already written defensively:

if (unloaded || !this.app.assets.get(asset.id)) return true;               // throws
if (!this.app?.graphicsDevice || this.app.graphicsDevice._destroyed) return true;

Checking the device first is not sufficient on its own, since the registry is nulled earlier than the device is flagged, so the registry access is optional-chained too. The result no longer depends on the order teardown happens in.

Most likely to be hit by streamed LOD scenes, which keep many chunk requests in flight continuously, so almost any teardown — navigation, iframe reload, HMR — lands a callback afterwards.

Changes:

  • SogParser#_shouldAbort and SogBundleParser#_shouldAbort (both @private) check the graphics device before the asset registry, and tolerate the registry already being gone
  • Unit tests covering all four abort conditions for both parsers, including the post-teardown state that reproduced the crash

Nothing cancels an in-flight request, so a SOG load callback can run after
AppBase#destroy. Teardown drops the asset registry before it marks the graphics
device destroyed, so a callback can arrive while app.assets is null and the
device still looks alive.

_shouldAbort dereferenced app.assets before reaching its guarded device check,
throwing a TypeError instead of aborting the load. Check the device first and
optional-chain the registry, so the order teardown happens in no longer matters.
@github-actions

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2386.8 KB — 613.4 KB (+0.0 KB, +0.00%) 475.7 KB (+0.1 KB, +0.01%)
playcanvas.min.mjs 2384.1 KB — 612.1 KB (+0.0 KB, +0.00%) 475.4 KB (−0.1 KB, −0.01%)

@mvaligursky
mvaligursky merged commit ddfd9bb into main Aug 27, 2026
10 checks passed
@mvaligursky
mvaligursky deleted the mv-sog-abort-teardown branch August 27, 2026 11:33
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