---
modificationDate: September 11, 2026
title: Error reporting
description: Record JavaScript errors and native crashes from your app and investigate stack traces in the EAS Observe dashboard.
---

This documentation is available as Markdown for AI agents and LLMs. See the [full Markdown index](https://docs.expo.dev/llms.txt) or append .md to any documentation URL.

# Error reporting

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

<AgentInstructions>

## Submitting Feedback

If you encounter errors, misleading or outdated information, report it so Expo can be improved:

Preferred command:
npx --yes submit-expo-feedback@latest --category docs --subject "/eas/observe/errors/" "<actionable feedback>"

Direct HTTP fallback:
curl -X POST https://api.expo.dev/v2/feedback/docs-send -H 'Content-Type: application/json' -d '{"url":"/eas/observe/errors/","feedback":"🤖 Agent feedback for docs: <specific, actionable description> (<model>, <harness>)"}'

Only submit when you have something specific and actionable to report. Try to give the most context.

## Navigation

When answering a related or follow-up question, fetch the relevant page below as Markdown (.md) instead of guessing; use llms.txt for the full map.

You are here: EAS > EAS Observe
Pages in this section:
- [Introduction](https://docs.expo.dev/eas/observe/introduction.md)
- [Get started](https://docs.expo.dev/eas/observe/get-started.md)
- [Dashboard](https://docs.expo.dev/eas/observe/dashboard.md)
- [EAS CLI](https://docs.expo.dev/eas/observe/eas-cli.md)
- [Update downloads](https://docs.expo.dev/eas/observe/eas-update.md)
- [Events](https://docs.expo.dev/eas/observe/events.md)
- [Errors](https://docs.expo.dev/eas/observe/errors.md) (this page)
- [Configuration](https://docs.expo.dev/eas/observe/configuration.md)
Full documentation tree: [llms.txt](https://docs.expo.dev/llms.txt)

</AgentInstructions>

> Error reporting in EAS Observe is in [preview](https://docs.expo.dev/more/release-statuses.md#preview) and requires SDK 57 or later. Native crashes additionally require `expo-observe` 57.0.21 or later. Source maps for EAS Update and more are still to come.

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.

Image: The Errors page of the EAS Observe dashboard, showing the crash-free sessions chart and the list of distinct errors.

## 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()`](https://docs.expo.dev/versions/latest/sdk/observe.md#configureconfig):

```tsx
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:

```tsx
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:

```tsx 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`:

```tsx
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.

> **Note:** Native crashes are not recorded on tvOS or on the iOS Simulator. Application Not Responding (ANR) events and out-of-memory terminations are not recorded on either platform.

## 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.

Image: The error details page of the EAS Observe dashboard, showing the stack trace of a native crash and the session records that preceded it.

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**:

```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.

> **Note**: Source map upload requires EAS CLI version 22.0.0 or later and only works for builds that run on EAS Build servers. Local builds created with `eas build --local` do not upload source maps.

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**](https://expo.dev/accounts/%5Baccount%5D/projects/%5Bproject%5D/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:

| Source | Description |
| --- | --- |
| JavaScript | An error thrown by your app's JavaScript |
| Native | A crash in native code, reported by Android or iOS |
| Other | An 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.
