Skip to main content
createApp returns a typed BootResult. A failure is a named { ok: false, reason, detail }, and mount() renders it on the crash screen instead of leaving a blank or dead app. Every anticipated failure has a reason; a thrown failure is caught and given one too.

Quick Start

1

Handle a failed boot

mount() checks booted.ok and renders the crash screen with the detail. There is nothing to configure — a named failure always reaches the screen.
2

Turn a throw into a reason

bootOrFail wraps createApp so an unanticipated throw — a StoragePort failure at settingsStore.load() — becomes the same typed shape rather than an uncaught rejection.

Every BootResult.reason

unknown_engine and protocol_mismatch are the two failures selectEngine anticipates and returns as a result. storage_unavailable is different in kind: it is a throw the boot never expected, turned into the same shape by bootOrFail so it renders the same way.

How a throw becomes a typed failure

createApp calls settingsStore.load() without a guard, so a StoragePort failure propagates as an exception. The chrome is already appended by the time boot runs, so an unhandled rejection would skip both the crash screen and the listener registrations — leaving a fully rendered app in which nothing happened. bootOrFail closes that gap.
The crash handler is installed in mount() before detectPlatform(), because detectPlatform reads window.localStorage and can itself throw SecurityError. See Architecture → Boot Order for the full ordering.

Architecture

Boot order and where the crash handler is installed.

Storage & Secrets

When the StoragePort is unavailable at boot.

Errors & Recovery

Failures that happen inside a turn, not at boot.

Engines

Where unknown_engine and protocol_mismatch come from.