This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Error reporting

Edit page

Record JavaScript errors and native crashes from your app and investigate stack traces in the EAS Observe dashboard.


The expo-observe library records JavaScript errors and native crashes from your app alongside its performance metrics. Errors are persisted on-device, batched, and dispatched on the next flush. They appear in the Errors page of the EAS Observe dashboard.

JavaScript errors

JavaScript errors are captured through three paths: unhandled errors are recorded automatically, render errors are caught by ObserveErrorBoundary, and handled errors can be reported with Observe.reportError.

Unhandled errors

Unhandled JavaScript errors are recorded automatically. The library installs a global error handler when it is first imported, so no setup is required. React Native's own behavior is unchanged: the red box still appears in development, and fatal errors still terminate the app in production.

To turn off automatic recording, set errorHandlingEnabled to false via configure():

import { Observe } from 'expo-observe'; Observe.configure({ errorHandlingEnabled: false, });

This only affects unhandled JavaScript errors. Errors caught by ObserveErrorBoundary, errors reported with Observe.reportError, and native crashes are still recorded.

Render errors

Without an error boundary, an error thrown while rendering is recorded by the global error handler as an unhandled error. Wrap a subtree with ObserveErrorBoundary to record it together with the React component stack and show a fallback UI in place of the subtree that threw:

import { ObserveErrorBoundary } from 'expo-observe'; export default function FeedScreen() { return ( <ObserveErrorBoundary fallback={({ error, resetError }) => <ErrorScreen error={error} onRetry={resetError} />}> <Feed /> </ObserveErrorBoundary> ); }

The fallback prop accepts a React element, null, or a function that receives the thrown error and a resetError callback. Calling resetError() clears the caught error and re-mounts the children, so they restart from a clean state.

To place a boundary around your whole app, pass errorBoundaryFallback to the ObserveRoot component instead of wrapping it manually:

src/app/_layout.tsx
import { ObserveRoot } from 'expo-observe'; export default function RootLayout() { return ( <ObserveRoot errorBoundaryFallback={<FallbackScreen />}> <Stack /> </ObserveRoot> ); }

Render errors that no boundary catches are still recorded by the global error handler.

Handled errors

Errors your code catches and recovers from reach neither the global handler nor an error boundary. Report them with Observe.reportError:

import { Observe } from 'expo-observe'; async function handleSync() { try { await syncCart(); } catch (error) { Observe.reportError(error); } }

reportError accepts any thrown value. An Error contributes its name, message, and stack trace. Any other value (a string, a plain object, a number) is stringified into the message without a stack trace.

Avoid Personally Identifiable Information (PII) in error messages. Everything you report is visible in the dashboard and is dispatched off-device.

Native crashes

A native crash terminates the app before your JavaScript can react to it, so the report is written on the device and dispatched the next time the app launches. Native crashes are recorded automatically on Android and iOS with expo-observe 57.0.21 or later, and no setup is required. They appear with the Native source in the dashboard.

On Android, EAS Observe records uncaught Java and Kotlin exceptions, including the Caused by chain. On Android 11 and later, it also reads the crash records the operating system keeps for your app, which covers crashes in native code such as SIGSEGV and SIGABRT.

On iOS, EAS Observe uses MetricKit to collect the crash reports the system produces for your app. These cover Mach exceptions such as EXC_BAD_ACCESS and EXC_BREAKPOINT, and Unix signals such as SIGSEGV, SIGBUS, and SIGTRAP. On iOS 17 and later, they also cover uncaught Objective-C and Swift exceptions.

Investigate an error

Click an error in the list to open its details. The page shows how often the error occurs, how many users it affects, when it was first and last seen, and how it splits across platforms.

Below the summary, Occurrence steps through the individual reports one at a time, each with the app version, device, and OS it came from. Stack trace shows the frames for the selected occurrence, and Before the crash lists the last session records that preceded it. Open Session timeline to see the full session. Use the Breakdown section to see which app versions, operating systems, devices, and countries the error affects, and click a value to filter the page by it.

The Occurrences tab lists every report in the group, so you can scan the versions and devices an error appears on.

Hand off to AI

Select Hand off to AI on the error details page to start investigating the error with a coding agent. It prepares a prompt that describes the error, its stack trace, the filters currently applied, and the breakdown by version, OS, device, and country. Send the prompt straight to Claude Code or Codex, or copy it and paste it into another assistant.

The agent uses the prompt to map the stack trace onto your source and to pull more context with the eas observe: commands, such as the session the crash came from.

Symbolicated stack traces

In a production app, your JavaScript is bundled and minified. Stack traces point at line and column positions in the generated bundle, not in your source files. A source map translates those positions back. When a source map is stored for a build, the dashboard shows the original file, line, and column for each frame, and links the build the error came from next to the stack trace.

If no source map is stored for the build, the dashboard shows the reported stack trace as-is. Frames then reference positions in the minified bundle, such as index.android.bundle:1:481231, and are difficult to map back to your code.

Upload source maps with EAS Build

To store a source map for each build, set uploadSourceMaps to true in the build profile in eas.json:

eas.json
{ "build": { "production": { "uploadSourceMaps": true } } }

With this setting, EAS Build uploads the source map produced when your app's JavaScript is bundled. Symbolication then works for every error reported from that build. No changes to your app code are required.

The source code embedded in the source map (sourcesContent) is removed before upload. Only file names and position mappings are stored. If the upload fails, the build still completes and shows a warning in the build logs.

Native stack traces

Source maps only apply to JavaScript. Native stack traces are not symbolicated in the dashboard.

On Android, frames from Java and Kotlin exceptions already carry the class, method, and line number. On iOS, frames are resolved to symbol names on the device where the crash happened, so you see function names but no file names or line numbers. A frame that cannot be resolved is shown as a binary name and an offset, such as MyApp + 19160.

View errors

Open your project and navigate to Observe > Errors.

The Crash-free sessions card shows the share of sessions in the selected time range that finished without a fatal error. It also shows crash-free users, fatal and non-fatal counts, and the number of affected users.

Distinct errors lists the errors recorded in the selected time range, grouped by error. The Source column tells you where an error came from:

SourceDescription
JavaScriptAn error thrown by your app's JavaScript
NativeA crash in native code, reported by Android or iOS
OtherAn error recorded by another part of the runtime

Filter the list by source, by severity (Fatal or Non-fatal), by platform, by environment, and by release. Fatal errors are reported on the app's next launch, so a recent crash can take time to appear.

Still to come

Error reporting is in preview, and the following are not available yet:

  • Source maps for EAS Update: errors from an app running an OTA update show the unsymbolicated stack trace.
  • Symbolication for native crashes: uploading debug symbols, such as Android ProGuard mappings and iOS dSYMs, is not supported yet.