skip to content

An EAS Update you published crashes at launch; what does expo-updates do on devices that downloaded it, and what should you do next?

level: seniorimportance: should knowfreq 42%

answer

  1. prevent bricking, not prevent crashes
  2. first render is the dividing line
  3. 10-second window after content appears
  4. 5-second wait for a newer update
  5. fall back to last working update

basics

~20 s

expo-updates' error recovery catches early fatal JS errors. If the update never rendered, it is marked failed and the app falls back to the last working update unless a newer one arrives within 5 s; once it has rendered, recovery only fetches a fix, then crashes.

solid answer

~50 s

expo-updates has a lightweight **error recovery** pipeline whose goal is to stop a bad update from bricking the app, not to prevent every crash. It only catches fatal JavaScript errors thrown before, or within 10 seconds after, the first view has rendered. If the error comes before the first render on the update's first launch on that device, the update is marked failed so it is never launched there again; expo-updates then waits up to 5 seconds for a newer update, reloads into it if it arrives, and otherwise falls back to the most recently successfully launched update, crashing only if none exists. If the first render already happened, it only tries to download a newer update in those 5 seconds and then crashes; the fix runs on the next open. So I would publish a tested fix forward quickly, because every affected device checks for it during recovery.

go deeper

for a junior

Know that expo-updates tries to stop a broken update from bricking the app, and that the real fix is publishing a corrected update quickly.

for a middle

Explain the first-render dividing line, the 10-second catch window and the 5-second wait for a newer update.

for a senior

Walk both paths precisely, including marking the update failed and falling back to the last working update, and plan the response: fix forward, roll back only when state allows.

for a principal

Weigh how much to trust client-side recovery versus staged rollouts and pre-release testing when deciding what may ship over the air.

## What error recovery is for **expo-updates** can run a JavaScript bundle published through **EAS Update** without a store review, so a broken update reaches devices fast. Its **error recovery** flow exists to make sure such an update cannot **brick** the app, meaning crash on every launch before the app gets a chance to download a fix. It is explicitly not a safety net against all crashes: users can still see a crash, and the Expo docs say the behaviour may change and should not be relied upon in place of testing. ## Which errors it catches Recovery only reacts to a **fatal JavaScript error** thrown early in the launch: - errors before the app's first view has rendered (the native "content appeared" event); - errors within **10 seconds** after that first render. An error thrown later than that is not caught at all: by then the app has had time to download a newer update, so expo-updates stays out of the way. This is why the docs recommend checking for updates very soon after launch, automatically or manually. ## Before the first render: roll back is considered safe If the error fires before content appeared, and this is the first time this update has launched on this device, the pipeline runs these steps in order: 1. The update is **marked failed** locally and will not be launched again on this device; a later `checkForUpdateAsync()` reports it as `updatePreviouslyFailed`. 2. A **5-second** timer starts and expo-updates checks for a newer update (unless `checkAutomatically` is `NEVER`). 3. If a newer update downloads within those 5 seconds, the app reloads into it immediately. 4. If there is none, it times out, or the new one also crashes, the app reloads into the **most recently successfully launched update**, which is the embedded bundle only if nothing newer ever launched. 5. If that also fails, or no older update exists, the original error is rethrown and the app crashes. Rolling back is only automatic here because, before the first render, the broken update has probably not had time to rewrite persisted state. ## After the first render: fix forward only If the error fires after content appeared (on this launch, or on an earlier launch of the same update on this device), expo-updates assumes the update may already have changed persisted data (AsyncStorage, files) in a way older code cannot read. It no longer rolls back: - it starts the same 5-second window and downloads a newer update if one exists; - then it rethrows the original error and the app crashes; - a newer update downloaded in that window launches the next time the user opens the app. ## The two paths side by side | | Error before first render, first launch | Error after first render | |---|---|---| | Update marked failed | Yes | No | | Looks for a newer update (5 s) | Yes | Yes | | Reloads into it immediately | Yes | No, it runs on next open | | Falls back to an older working update | Yes | No | | Ends in a crash | Only if nothing else works | Yes | ## Seeing it happen in the field A device that went through recovery leaves a trail. `Updates.readLogEntriesAsync()` returns expo-updates' recent client log entries (the last hour by default), with codes such as `JSRuntimeError` and `UpdateFailedToLoad`, which a release build can forward to your own logging on the next successful launch. On the server side, an update's details page in EAS shows how many devices ran it and how many installs failed, which is often the first sign that recovery is firing. Neither replaces testing the update on a production-like build before publishing. ## What you should do - **Fix forward as fast as a tested fix allows.** Every affected device looks for a newer update during recovery, so publishing the fix is what unblocks users stuck on the after-render path. - **Only roll back to an older update if it is known to be safe** with whatever state the broken update may have written; that is a server-side action with its own tooling. - **Keep update checks near launch.** With `checkAutomatically` set to `NEVER` the recovery flow skips its own check, so a manual-check app must check early. - **Read the crash correctly.** On iOS the native backtrace points into expo-updates' recovery code, but the underlying error originated in JavaScript.

  • Why does expo-updates roll back automatically only when the crash happens before the first render?
    Rolling back runs older code against whatever the broken update left on the device. Before the first view renders, the update has very likely not written persisted data yet, so falling back is treated as safe. After it, the update may have migrated storage in a way older code cannot read, so expo-updates only fetches a fix and lets the app crash.
  • Why does a crash thirty seconds into a session never trigger error recovery?
    Recovery stops listening 10 seconds after content appears. By then the default launch-time check has had its chance to download a newer update, so expo-updates leaves later errors to normal crash handling. The fix still arrives through the usual check on the next launch.

saying these in an interview costs you the question

  • Claims error recovery always rolls back to the embedded bundle
  • Thinks expo-updates catches crashes at any point in a session
  • Believes a failed update keeps being retried on the same device
  • Treats the iOS backtrace in expo-updates as a native expo-updates bug
  • Relies on error recovery instead of testing updates before publishing