iOS 26.2 (WebKit): page process repeatedly killed every ~30–60 s once the PowerSync WASM module has loaded — reproduced on @powersync/web 1.38.3 and 2.2.0 #1081
Description
TL;DR: On an iPhone 14 Pro running iOS 26.2.1, the Safari/WebKit page process is killed every ~30–90 s (silent full page reload, occasionally Safari's "This webpage was reloaded because it repeatedly caused problems") in any web app session where the PowerSync WASM module has been loaded — even after close()-ing the database. Sessions where the module is never imported are completely stable. Reproduced on both @powersync/web 1.38.3 (wa-sqlite 1.7.0) and 2.2.0 (@journeyapps/wa-sqlite 2.0.3, default settings). No issues on Android Chrome or desktop browsers with the same app and account.
Environment
Device: iPhone 14 Pro, iOS 26.2.1 (first noticed after updating to iOS 26.2.x; same app was fine before)
Browser: Safari — both as an installed PWA (standalone) and in a regular Safari tab
SDK: reproduced on @powersync/web@1.38.3 + @journeyapps/wa-sqlite@1.7.0, and on @powersync/web@2.2.0 + @journeyapps/wa-sqlite@2.0.3
Bundler: Vite 5, worker: { format: 'es' }, optimizeDeps.exclude: ['@journeyapps/wa-sqlite', '@powersync/web'], SDK loaded via dynamic import()
VFS: default (IndexedDB-based); no OPFS, no encryption
Backend: PowerSync Cloud + Supabase (JWT), small dataset (< 1 MB, a dozen synced tables)
Not affected: Android Chrome, desktop Chrome/Edge — same app, same account, zero incidents
Symptom
With a logged-in session (SDK active), the page dies without any lifecycle events (pagehide/visibilitychange never fire) roughly every 30–90 s while the app is in the foreground and being used normally. The page then reloads from scratch. Sometimes Safari shows "This webpage was reloaded because it repeatedly caused problems." Nothing appears in the console before death — it looks like the OS/WebKit killing the web content process, not a JS error.
How we measured (watchdog)
We instrumented the app with a heartbeat watchdog: a localStorage heartbeat every 5 s carrying route, session state, whether the PowerSync DB instance was open, and threading mode; pagehide/visibilitychange write a "goodbye" marker. On boot, a heartbeat without a goodbye = the page died without permission. (It has multi-tab false-positive protection and is biased to undercount.)
Sample from a run on 2.2.0 (fresh origin, default settings, single-thread mode on):
2026-08-24T15:22:58Z · /mas · lived 81s · session · DB closed
2026-08-24T15:29:17Z · /habitos · lived 377s · session · DB open
2026-08-24T15:30:23Z · /habitos · lived 60s · session · DB open
2026-08-24T15:31:21Z · / · lived 55s · session · DB open
2026-08-24T15:32:31Z · / · lived 65s · session · DB open
2026-08-24T15:33:35Z · / · lived 59s · session · DB open
2026-08-24T15:34:20Z · /mas · lived 40s · session · DB open
2026-08-24T15:35:17Z · /mas · lived 55s · session · DB closed
Earlier runs on 1.38.3 show the same shape (cycles of ~30–65 s; 60+ events collected across sessions).
What the evidence isolates
A/B/A on session: logged in → crash every 30–60 s on any route; logged out (SDK never loads) → zero crashes over long sessions; log back in → crashes return. Repeated cleanly.
Not the open DB handle: we added lifecycle management that disconnect()s and close()s the database when no data screen needs it. Crashes continue, including events recorded with the DB closed (see log). Once the WASM module has been loaded in the page's lifetime, the page is doomed.
Module-level A/B (repeated 3×): if the screens/hooks that would trigger the SDK's dynamic import() are disabled (so the WASM module is never fetched/compiled), the same logged-in session is completely stable. Re-enabling a single such screen brings the crash loop back within a minute.
Single-threaded mode doesn't fix it: useWebWorker: false + enableMultiTabs: false lengthens the cycle (~30 s → ~45–65 s) but the kills continue. (Also relevant: we checked our shipped glue for SharedArrayBuffer/new Worker usage — the build doesn't use shared WASM memory, so emscripten-core/emscripten#25905, the known iOS 26.2 shared-memory regression, doesn't seem to apply.)
Upgrade didn't change it: 1.38.3 → 2.2.0 on a fresh origin (default HTTP streaming, default open options) reproduces the identical pattern.
Our working hypothesis
The periodicity and the "module loaded ⇒ death, even when idle/closed" signature smell like WebKit's memory-pressure killer reacting to something that recurs as long as the compiled module is alive — possibly re-JIT/recompilation spikes of the large wasm module, in the spirit of what's described in rhashimoto/wa-sqlite#94 (Apple-silicon/iOS JIT memory spikes). But we have no way to confirm from JS, since the process dies without a trace.
Questions
Are there other reports of iOS 26.2.x killing pages with @powersync/web loaded? Anything known on your side?
Is there a recommended configuration to reduce WASM/JIT memory footprint on iOS (different VFS, interpreter-ish build, smaller module, prepared-statement cache off, etc.) worth A/B-testing?
Any way you'd suggest to get more diagnostic signal out of WebKit here (we can run any instrumented build on the affected device and report back — we have the watchdog + a willing tester)?
Happy to provide more data, run experiments on the device, or test branches. Thanks!
Activity
simolus3 commentedon Aug 24, 2026
I'm not aware of another report of this. Did this only happen on one device or is this a general iOS problem you're running into? Also, did you test on macOS and were unable to reproduce the issue there (asking because debugging on macOS is probably easier)?
For what it's worth, the module is only loaded in dedicated workers (assuming multi-tab support is disabled, which it is by default on Safari) and we're supposed to close those workers when the database is closed. It might be worth double-checking with a remote inspector to check whether a worker is still active after closing the database. It would be surprising if a worker thread that doesn't even exist anymore could bring the whole tab down.
PLS360 commentedon Aug 24, 2026
Thanks for the quick response!
One device or general? Single device so far: an iPhone 14 Pro on iOS 26.2.1, running the app as an installed PWA. It's the only iOS device we have regular access to, so we can't yet say whether it's general. The same build on Android (Chrome) and desktop Chrome never shows the issue.
macOS: not tested yet — we don't own a Mac, but we're arranging access to one specifically to run a remote Web Inspector session against the iPhone. We'll check whether any worker is still alive after close(), as you suggest, and report back here.
One data point that may matter for the worker theory: we reproduce the crashes in both configurations — the default one (dedicated worker) and with useWebWorker: false + enableMultiTabs: false (a workaround flag we added for iOS), where as far as we understand the module is loaded on the main thread. With the workaround the crash cycle lengthens (~30s → ~45s between reloads) but doesn't stop. In fact, most of the crash events in our logs were recorded under the main-thread configuration — including the two events recorded with the database already closed. So whatever survives close() doesn't seem to be (only) a worker.
We'll follow up with the inspector findings.