Quick answer: A Construct 3 mobile export runs your event sheets as JavaScript in a Cordova WebView over a native bridge, so a crash can be a WebView exception, a native plugin fault, or a memory eviction. Catch JavaScript errors with onerror and unhandledrejection, detect native crashes with a dirty shutdown marker, and tag every report with plugin and WebView versions so the layer at fault is never ambiguous.

A Construct 3 mobile export is a web game wrapped in a Cordova shell that runs your project inside the platform WebView. Your event sheets execute as JavaScript, but they sit on top of a native bridge, so a crash can originate in your logic, in a Cordova plugin, or in the WebView itself. Separating those three layers is the whole job of mobile crash reporting here, because a JavaScript exception leaves the app alive while a native plugin fault kills the host process entirely. This post covers catching errors on both sides of the bridge, detecting the native crashes that no JavaScript handler can see, and surviving the fragmentation of real Android and iOS hardware in the field.

The wrapped WebView execution model

Construct 3 runs your event sheets as JavaScript inside a single WebView hosted by a thin Cordova application. Most logic errors, like referencing a destroyed instance or a malformed action, throw inside that WebView and never touch native code. These look like ordinary browser exceptions and are recoverable in the sense that the host process survives, even if your game loop stalls and the player sees a frozen screen. They are also the easiest class to capture, because the browser error hooks fire normally.

Native plugin calls are different. When an event sheet invokes a Cordova plugin for in app purchases, camera access, or push notifications, control crosses the bridge into Java, Kotlin, or Objective C. A fault there can crash the host process entirely, and the WebView dies with it before any JavaScript handler can run. Your crash strategy must therefore catch two distinct failure classes that present completely differently to the player and require completely different capture mechanisms, which is why a single web error listener is never enough on mobile.

Catching errors on both sides of the bridge

For the JavaScript side, register window.onerror and an unhandledrejection listener as early as the runtime allows, before your first layout loads. Construct event sheets do a lot of work in promises through the async action system, so rejections are a common and easily missed failure mode. Buffer the current layout name and the last triggered event group as breadcrumbs so a detached rejection still carries context about what the player was doing when the promise failed, rather than arriving as a bare stack with no story attached.

For the native side you cannot rely on a JavaScript listener, because the process may be gone before any handler runs. Persist a small marker to disk on each layout transition, then on the next clean launch detect that the previous session ended without a graceful shutdown and report it as a probable native crash. Correlating that marker with the last plugin call you logged usually points straight at the offending bridge call, turning an invisible process death into a report that names the purchase or camera plugin that took the app down.

Cordova plugins as a crash surface

Plugins are the most common source of hard crashes in Construct mobile exports because they run native code you did not write and cannot easily inspect. An outdated billing plugin, a permission requested too late, or a camera plugin that does not handle a denied permission can all abort the host process. Pin your plugin versions, and record the exact plugin set and versions in your report metadata so a regression after a plugin bump is obvious rather than a mystery you debug from scratch on the next release.

Permission denials deserve special handling. On modern Android and iOS a denied camera or storage permission does not always throw a catchable error; sometimes the plugin returns an unexpected null that your event sheet then dereferences several actions later. Logging the permission state alongside the plugin call turns a confusing downstream null reference into a clear story about a missing grant, which is otherwise one of the hardest mobile bugs to reproduce because it depends on a choice the player made in a system dialog you never see.

Device fragmentation and WebView versions

The WebView is not part of your app; it is supplied by the operating system and updated independently. An Android device on an old System WebView can lack a JavaScript feature your event sheets assume, and an iOS WKWebView can enforce stricter memory limits than your test devices. Always capture the WebView or Safari version, the OS version, and the device model, because the same build genuinely behaves differently across this matrix and a crash isolated to one WebView version is a compatibility issue, not a logic bug.

Memory pressure is acute on low end Android. The system can kill a backgrounded WebView app without warning, and to your reporting that looks like a silent session end rather than an exception. Recording peak memory and whether the app was backgrounded before the failure lets you distinguish an out of memory eviction from a logic bug, which keeps your triage queue honest. Otherwise you will spend time hunting a phantom crash that is really the operating system reclaiming resources from a heavy scene on a cheap phone.

Setting it up with Bugnet

Because the Construct runtime is web based, you integrate the Bugnet web SDK by adding its initialization to the exported project, then rebuild the Cordova wrapper. The SDK installs the WebView level handlers for you and queues reports to disk so a crash during a flaky cellular connection is delivered on the next launch rather than lost. Its in game report button captures the current layout and game state automatically, so players reporting a non fatal glitch hand you the same context an exception would, without you writing a custom form.

Bugnet folds the JavaScript failures together with occurrence grouping and surfaces the breadcrumb trail, the layout, and the device profile, so you can see at a glance whether a fault is universal or limited to one Android API level. For suspected native crashes flagged by your dirty shutdown marker, you attach the last plugin invocation as a custom field, which lets you filter every report that touched the purchase plugin and confirm a bad release fast. Occurrence counts then tell you which crash is worth the next patch and which is a rare edge case.

Operating across store releases

Mobile updates roll out slowly through the app stores, so multiple versions of your game are live at once. Stamp every report with the store build number and not just the Construct project version, because the wrapper and plugin set can change without the game logic changing. Attributing a crash spike to the correct store build is impossible otherwise, and on mobile you cannot force players onto a fix the way you can with a server side change.

Make a post release habit of reviewing top signatures split by OS and WebView version. A fix that resolves a fault on current devices may do nothing for the long tail of old WebViews, and a plugin update can introduce a native crash that only your dirty shutdown markers reveal. A short check of ingestion and signature churn after each submission catches these before reviews suffer, which on mobile is the channel where a single common crash quietly drains your rating one frustrated player at a time.

Treat a Construct 3 mobile export as two stacked systems: an event sheet WebView and a Cordova native shell. Catch JavaScript exceptions in the WebView, detect native crashes with a dirty shutdown marker, and tag every report with plugin and WebView versions.