Cap composited .spd image size to avoid OOM on a corrupted/outlier tile - #39
Open
philips-clanker wants to merge 1 commit into
Open
Cap composited .spd image size to avoid OOM on a corrupted/outlier tile#39philips-clanker wants to merge 1 commit into
philips-clanker wants to merge 1 commit into
Conversation
toImage() sized its output against the tile-grid bounding box across every tile in the file, with no sanity check at all. A single tile whose tid is far from the rest (a corrupted file, or a stray mark placed far off on Atelier's much larger virtual canvas - see TILE_ID_STRIDE's doc comment) blows that bounding box out arbitrarily far, and toImage() would allocate accordingly - gigabytes for what should be a small image. On desktop that's slow or an OOM in one tab. On a memory-constrained mobile WebView it's a hard crash, not a catchable one - this is the root cause of supernote-obsidian-plugin#147's iOS "boot loop": since the host app restores the last-open file on relaunch, one bad .spd file crashes it again immediately, every time, with no way out short of clearing workspace state. Adds a 32-megapixel ceiling (real device files observed so far top out around 1920x2560, ~4.9 megapixels - see atelier.test.ts) and throws a descriptive error instead of allocating past it. The main plugin's SupernoteAtelierView already catches errors from this path and renders them in-view (see updateImage()'s try/catch), so this turns a native crash into a normal, visible "couldn't open this file" error.
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Candidate fix for supernote-obsidian-plugin#147 (crash/"boot loop" opening certain notes on iOS). Note: the reporter has since clarified the crashing file in their case is a
.notefile, not.spd, so this fix addresses a real, independently-verified bug found while investigating that report, but may not be the cause of #147 itself - see that issue for the ongoing investigation into the.notepath.SupernoteAtelier.toImage()sizes its output image against the tile-grid bounding box computed across every tile in the file (computeGridBounds()), with no sanity check before allocating. A single tile whosetidis far from the rest - a corrupted file, or in principle a stray mark placed far off on Atelier's own much-larger virtual canvas (seeTILE_ID_STRIDE's doc comment) - blows that bounding box out arbitrarily far, andnew Image(width, height, ...)allocates accordingly: potentially gigabytes for what should be a small composite.On desktop that's slow or an OOM in one tab. On a memory-constrained mobile WebView (iOS in particular) that's a hard, non-catchable crash - exactly the shape of bug that would produce a "boot loop": the host reopens the last file on relaunch, so one bad
.spdfile crashes it again immediately, every time.Changes
atelier.test.ts), throwing a descriptiveErrorinstead of allocating past it.SupernoteAtelierView.updateImage()already wraps this call in try/catch and renders errors in-view, so this turns a native crash into a normal, visible "couldn't open this file" error rather than requiring any change on that side.Test plan
npm run build- cleannpx eslint src/atelier.ts tests/atelier.test.ts- 0 errorsnpx vitest run- 41 passed (39 previously + 2 new).spdSQLite database (viasql.jsdirectly, not a real device export) with one tile 10 million grid-columns from the origin, confirmingtoImage()throws rather than allocating