---
title: React Native JavaScript error reporting
source: https://docs.newrelic.com/docs/mobile-monitoring/new-relic-monitoring-react-native/react-native-agent-js-error-reporting
---

By default, the New Relic React Native agent captures JavaScript errors and unhandled promise rejections and reports them as [`MobileJSError` events](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-mobile/mobile-sdk/record-errors/#react). You can view these errors in the mobile monitoring UI, query them with NRQL, and chart them in dashboards.

To make the stack traces in `MobileJSError` events human-readable, the agent needs the source map that corresponds to the JavaScript bundle running in your app. When you configure your New Relic User API key and application token correctly, the agent uploads the source map for you automatically after each build. If you can't upload automatically, or you ship JavaScript-only updates with CodePush or another over-the-air (OTA) service, you can upload source maps manually.

> #### ⚠️ IMPORTANT
>
> Source map upload uses a [User API key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-key) in addition to the application token. The application token identifies your app, but it doesn't authenticate a specific user, so it can't safely authorize an upload on its own. The User API key ties the request to an authenticated New Relic user, which prevents anyone who only has the (less sensitive) application token from uploading or overwriting your source maps. The User API key and the application token must belong to the same New Relic account.

> #### 💡 TIP
>
> JavaScript error reporting is enabled by default. Set the `jsErrorReportingEnabled` [configuration setting](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-mobile/mobile-sdk/configure-settings/#react) to `false` to disable recording of `MobileJSError` events entirely.

## Set up automatic source map upload [#automatic-upload]

To upload source maps automatically, provide your New Relic User API key and application token. Because a React Native app builds separately for each platform, you configure the key differently on Android and iOS. Set it up for each platform you ship.

> #### 💡 TIP
>
> If you build with [EAS Build](https://docs.expo.dev/build/introduction/), the steps below get wiped out on every prebuild. Use the [config plugin's EAS setup](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-monitoring-react-native/configure-eas-build-react-native) instead so automatic upload survives Continuous Native Generation.

Before you begin, get the following from the same New Relic account:

-   A [User API key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-key).
-   Your mobile [application token](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-mobile/maintenance/viewing-your-application-token) (the same token you pass to `NewRelic.startAgent()`).

### Android [#automatic-upload-android]

Add your User API key to the `newrelic.properties` file in your project:

```properties
com.newrelic.api_key=<YOUR_USER_API_KEY>
```

Replace `<YOUR_USER_API_KEY>` with your User API key. The agent already knows the application token from `NewRelic.startAgent()`. When both values are valid, the agent generates and uploads the Android source map to New Relic automatically after each release build.

> #### 💡 TIP
>
> Automatic upload only runs for release builds by default. To get automatic source map upload for debug builds too, add `Debug` to the `uploadMapsForVariant` setting in your [New Relic Gradle plugin configuration](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-mobile-android/install-configure/configure-new-relic-gradle-plugin), for example `uploadMapsForVariant("Release", "Debug")`. Otherwise, [upload the debug build's source map manually](#manual-upload).

### iOS [#automatic-upload-ios]

On iOS, a build-phase script (`upload-react-native-sourcemap`) included in the `dsym-upload-tools` folder — the same folder used for [dSYM uploads](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-mobile-ios/configuration/upload-dsyms-bitcode-apps) — uploads the source map. You pass the User API key and application token as arguments to that script.

1.  If you haven't already set up dSYM uploads, copy the `dsym-upload-tools` folder into your project's `SRCROOT` (typically your `ios` folder).
2.  In Xcode, select your target, open the **Build Phases** tab, and add a **New Run Script Build Phase**. Drag it to run after the "Bundle React Native code and images" phase.
3.  Add the following to the run script, replacing the placeholders with your User API key and application token:

    ```bash
    ARTIFACT_DIR="${BUILD_DIR%Build/*}"
    SCRIPT=`/usr/bin/find "${SRCROOT}" "${ARTIFACT_DIR}" -type f -name upload-react-native-sourcemap | head -n 1`
    /bin/sh "${SCRIPT}" "YOUR_USER_API_KEY" "YOUR_APP_TOKEN"
    ```

> #### 💡 TIP
>
> Don't commit credentials to version control. Store the User API key and application token in an `.xcconfig` file or your CI/CD system's secrets, then reference them in the run script (for example, `"${NR_USER_API_KEY}" "${NR_APP_TOKEN}"`). Add `--debug` as a third argument to write verbose output to `upload_sourcemap_results.log`.

The iOS script runs only for **Release** builds and skips simulator builds. If either value is missing or invalid, the agent won't upload the source map, and JavaScript error stack traces will remain unsymbolicated. In that case, [upload the source map manually](#manual-upload).

## Manually upload a source map [#manual-upload]

You can upload a source map directly to the New Relic symbol ingest API. This is useful when automatic upload isn't possible, or when you release JavaScript-only updates through [CodePush or other OTA services](#codepush).

Use the following **cURL** template:

```bash
curl -X POST "https://symbol-ingest-api.service.newrelic.com/v1/react-native/sourcemaps" \
     -H "Api-Key: $NR_USER_API_KEY" \
     -H "X-APP-LICENSE-KEY: $NR_APP_TOKEN" \
     -F "sourcemap=@./index.android.bundle.map" \
     -F "jsBundleId=<JS_BUNDLE_ID>" \
     -F "appVersion=1.0.5" \
     -F "sourcemapName=index.android.bundle.map"
```

Replace the following:

-   `$NR_USER_API_KEY` is a valid New Relic [User API key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-key).
-   `$NR_APP_TOKEN` is your mobile monitoring [application token](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-mobile/maintenance/viewing-your-application-token).
-   `<JS_BUNDLE_ID>` is the unique build identifier reported by the agent for the JavaScript session (see [Retrieve the jsBundleId](#retrieve-bundle-id)).
-   `appVersion` is the native application version the bundle targets (for example, `1.0.5`).

> #### 💡 TIP
>
> For accounts on New Relic's EU data center, use the EU endpoint instead: `https://symbol-ingest-api.service.eu.newrelic.com/v1/react-native/sourcemaps`.
>
> For accounts on New Relic's Japan data center, use the Japan endpoint instead: `https://symbol-ingest-api.service.jp.newrelic.com/v1/react-native/sourcemaps`.

### Upload API reference [#upload-api]

#### Endpoint [#endpoint]

| Property     | Value                                                                       |
| ------------ | --------------------------------------------------------------------------- |
| Method       | `POST`                                                                      |
| URL          | `https://symbol-ingest-api.service.newrelic.com/v1/react-native/sourcemaps` |
| Content-Type | `multipart/form-data`                                                       |

#### Headers [#headers]

| Header              | Required | Description                                                                                                                                                                              |
| ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Api-Key`           | Yes      | A valid New Relic [User API key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#user-key). It must belong to the same account as the application token.              |
| `X-APP-LICENSE-KEY` | Yes      | The application token for the mobile app.                                                                                                                                                |
| `X-Telemetry-Data`  | No       | Telemetry information about the bundler, source map names, and sizes. When a source map exceeds 200 MB unzipped, the agent doesn't send the file and instead transmits this header only. |
| `Content-Type`      | Yes      | Must be `multipart/form-data`.                                                                                                                                                           |

#### Request body (multipart form data) [#request-body]

| Field           | Type   | Required | Description                                                                                                                               |
| --------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `sourcemap`     | File   | No       | The source map file (`.map` or `.bundle`). Can be gzipped. Maximum 200 MB unzipped (see [File size limitations](#file-size-limitations)). |
| `sourcemapName` | String | No       | Name of the source map file. Maximum 255 characters.                                                                                      |
| `jsBundleId`    | String | Yes      | Unique build identifier (for example, a SHA or ID). Maximum 255 characters.                                                               |
| `appVersion`    | String | Yes      | Application version (for example, `1.5.2`). Maximum 255 characters.                                                                       |

> #### ⚠️ IMPORTANT
>
> If the source map file exceeds 200 MB unzipped, the agent won't send the file. Instead, it transmits the `X-Telemetry-Data` header so New Relic can still track that a build occurred. For details, see [File size limitations](#file-size-limitations).

#### Responses [#responses]

Responses use `Content-Type: application/json`.

| HTTP status                 | Description                                                                                                                                                                                                                                                                                                                                                                                         |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `201 Created`               | The upload succeeded. The response body contains the source map metadata: ```json {   "sourcemapMetaData": {     "entityGuid": "<SOURCEMAP_ENTITY_GUID>",     "accountId": "<YOUR_ACCOUNT_ID>",     "applicationId": "<YOUR_APPLICATION_ID>",     "sourcemapName": "app.min.js.map",     "appVersion": "12424",     "JSBundleId": "87124712",     "createdAt": "2025-11-27T10:32:44.586Z"   } } ``` |
| `400 Bad Request`           | Validation failed, for example missing fields, an invalid JSON schema in the file, or a malformed request. Example: `{"message": "Validation failed: Source map version must be 3."}`                                                                                                                                                                                                               |
| `401 Unauthorized`          | The API key is valid, but the `X-APP-LICENSE-KEY` belongs to a different account (cross-account protection). Example: `{"message": "User not authorized"}`                                                                                                                                                                                                                                          |
| `403 Forbidden`             | The `Api-Key` doesn't have the required capability. Make sure your User API key belongs to a user with mobile entity view permissions. Example: `{"message": "User not authorized"}`                                                                                                                                                                                                                |
| `404 Not Found`             | The application token provided in the header doesn't exist. Example: `{"message": "applicationToken is invalid"}`                                                                                                                                                                                                                                                                                   |
| `413 Payload Too Large`     | The unzipped source map file exceeds 200 MB. Example: `{"message": "File too large.  Max size is currently 200 MiB"}`                                                                                                                                                                                                                                                                               |
| `500 Internal Server Error` | A generic, unrecoverable error occurred on the server side. Example: `{"message": "Unexpected error occured during upload process because of {error}"}`                                                                                                                                                                                                                                             |

## Upload source maps for CodePush and OTA updates [#codepush]

When you use CodePush or another OTA update service, the JavaScript bundle version diverges from the native binary version. Each time you push a JavaScript update, upload the new source map so that `MobileJSError` events remain readable in New Relic.

To symbolicate an OTA update, the upload must use:

-   A unique `jsBundleId` that matches the ID the agent reports during the JavaScript session.
-   The correct `appVersion`, which is the native version the bundle targets.

You can upload the source map with a script in your CI/CD pipeline or manually with cURL.

### Method 1: Automated upload via script [#codepush-script]

New Relic provides a Node.js helper script that you can run in your CI/CD pipeline immediately after the `appcenter codepush release-react` command.

```bash
# Example integration
appcenter codepush release-react -a <Owner>/<App>
node upload-nr-sourcemap.js --bundle android/index.android.bundle --map android/index.android.bundle.map --bundleId <NEW_ID>
```

### Method 2: Manual upload via cURL [#codepush-curl]

If you prefer not to use the script, upload the source map (unzipped or zipped) to the symbol ingest API with cURL:

```bash
curl -X POST "https://symbol-ingest-api.service.newrelic.com/v1/react-native/sourcemaps" \
     -H "Api-Key: $NR_USER_API_KEY" \
     -H "X-APP-LICENSE-KEY: $NR_APP_TOKEN" \
     -F "sourcemap=@./index.android.bundle.map" \
     -F "jsBundleId=CODE_PUSH_ID_HERE" \
     -F "appVersion=1.0.5" \
     -F "sourcemapName=index.android.bundle.map"
```

For the full list of headers, body fields, and responses, see the [upload API reference](#upload-api).

### Retrieve the jsBundleId [#retrieve-bundle-id]

The `jsBundleId` used for the upload must match the bundle ID the agent reports for the JavaScript session. For CodePush releases, use the CodePush deployment or release identifier as the `jsBundleId` so the uploaded source map maps to the bundle running in your users' apps.

> #### 💡 TIP
>
> To verify, audit, or remove source maps you've uploaded, see [List and delete React Native source maps](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-monitoring-react-native/list-delete-react-native-source-maps).

## File size limitations [#file-size-limitations]

Source map files must be under 200 MB unzipped to be stored for symbolication.

Our build scripts automatically gzip the `.map` file before upload to reduce transfer size, but the build checks the 200 MB limit against the unzipped file. If the unzipped `.map` file exceeds 200 MB, the agent doesn't upload the file, which prevents build timeouts and ingestion errors.

In these cases, the script sends build telemetry (metadata) instead of the file. This lets New Relic track that a build occurred, even though symbolication isn't available for that specific version. As a result, `MobileJSError` events for that build show unsymbolicated (minified) stack traces.

If your source map is larger than 200 MB unzipped, [reach out to New Relic Support or file a feature request](#request-support). There's no way to raise this limit yourself.

## Troubleshoot source map uploads [#troubleshoot]

If your `MobileJSError` stack traces aren't symbolicated, your source map may have exceeded the [200 MB unzipped size limit](#file-size-limitations). Use the following steps to confirm the cause and request help. For more troubleshooting tips and frequently asked questions, see [Troubleshoot React Native source maps and JavaScript errors](https://docs.newrelic.com/docs/mobile-monitoring/new-relic-monitoring-react-native/troubleshoot-react-native-source-maps).

### Confirm whether the file or telemetry was uploaded [#confirm-upload]

A successful build doesn't always mean a successful file upload. If your build script completes with a `Success` message but your source map is larger than 200 MB unzipped, check your console logs. You'll see a message indicating that the agent sent telemetry instead of the source map file.

### Check the unzipped file size [#check-zip-size]

Check the size of your source map file to verify whether you're near or over the limit:

```bash
# Check the size of the unzipped source map
ls -lh index.android.bundle.map
```

If the file is near or above 200 MB, the source map can't be uploaded for symbolication.

### Request support for large source maps [#request-support]

If your unzipped source map exceeds the 200 MB limit, there's no way to reduce it on your side or raise the limit yourself. Do the following to let us know this limit affects you:

-   [Contact New Relic Support](https://support.newrelic.com/).
-   File a feature request to raise your source map size limit.
