---
title: "Uploading Symbols"
description: "Get Shorebird's engine symbols from the console or a download URL, upload them and your Dart symbols to your crash reporter, and read crash traces from patched apps."
---

Shorebird uses a fork of Flutter to build your app. This means Shorebird has built its own copy of Flutter’s engine, which means that the symbols included in the Flutter.framework or libflutter.so are slightly different from the upstream Flutter engine.

To see C++ symbols from Shorebird’s fork of Flutter’s engine in your crash reports, upload the symbols to your crash reporting tool.

Engine symbols and Dart symbols are separate uploads

There are two kinds of symbols:

* **Shorebird engine symbols** cover native engine frames from `libflutter.so` on Android and `Flutter.framework` on iOS. On iOS, get them from the console or the [download URL](#symbols-for-ios). On Android, use the [download URLs](#symbols-for-android).
* **Dart symbols** cover Dart frames. You only need them if you build with `--obfuscate` or `--split-debug-info`. See [Dart symbols for obfuscated builds](#dart-symbols-for-obfuscated-builds).

A mistake to avoid is uploading only the `--split-debug-info` output with `firebase crashlytics:symbols:upload`. Those files don’t contain Shorebird’s engine symbols, so native engine crashes stay unsymbolicated. To make both Dart and native crashes readable, upload both kinds of symbols. See [Uploading engine symbols to Crashlytics](#uploading-engine-symbols-to-crashlytics).

Upload Dart symbols as part of each build. Some tools only symbolicate crashes reported after the upload. For example, Crashlytics needs Dart symbols uploaded before an obfuscated build reports a crash.

Google provides instructions for how to integrate various crash reporting tools into your Flutter app:

<https://docs.flutter.dev/cookbook/maintenance/error-reporting>

## Getting symbols from the console

For iOS and macOS releases, the “Artifacts” tab of the release page in Shorebird’s console links to the Flutter engine’s dSYM, next to your release’s build artifacts. To see this tab, you need the [Developer role](/account/orgs#developer) or higher in the organization that owns the app. The console doesn’t link to Android engine symbols. Use the [download URLs](#symbols-for-android) instead.

The current URL format for this tab is:

```
https://console.shorebird.dev/orgs/<org-id>/apps/<app-id>/releases/<release-id>/artifacts
```

## Manual instructions (only needed for CI or scripts)

If you need a way to download symbols as part of a CI pipeline it’s possible with the following information:

For a given Flutter version you will need the Flutter engine hash to download the symbols. This hash is displayed in `shorebird doctor` output:

```
$ shorebird doctor
Shorebird 1.6.136 • git@github.com:shorebirdtech/shorebird.git
Flutter 3.47.9 • revision 8352242a910a1f79747cb1c6d2aa02edc5f3fc12
Engine • revision eaeb4bdcae57b971d6dd7507a3b53e61676b8330
```

In this case, `eaeb4bdcae57b971d6dd7507a3b53e61676b8330` is the engine hash.

Download the symbols from `storage.googleapis.com/download.shorebird.dev`, which holds Shorebird’s engine builds. The URLs below use it. Don’t use `download.shorebird.dev` for symbols: for some files it redirects to the upstream Flutter engine’s symbols, which don’t match Shorebird’s engine.

### Symbols for iOS

Assuming engine hash `eaeb4bdcae57b971d6dd7507a3b53e61676b8330`, the iOS symbols can be downloaded from the following URL:

<https://storage.googleapis.com/download.shorebird.dev/flutter_infra_release/flutter/eaeb4bdcae57b971d6dd7507a3b53e61676b8330/ios-release/Flutter.framework.dSYM.zip>

For some older Flutter versions, the file is named `Flutter.dSYM.zip` instead of `Flutter.framework.dSYM.zip`.

### Symbols for Android

Assuming engine hash `eaeb4bdcae57b971d6dd7507a3b53e61676b8330`, the Android symbols can be downloaded from the following URLs:

<https://storage.googleapis.com/download.shorebird.dev/flutter_infra_release/flutter/eaeb4bdcae57b971d6dd7507a3b53e61676b8330/android-arm64-release/symbols.zip> <https://storage.googleapis.com/download.shorebird.dev/flutter_infra_release/flutter/eaeb4bdcae57b971d6dd7507a3b53e61676b8330/android-arm-release/symbols.zip> <https://storage.googleapis.com/download.shorebird.dev/flutter_infra_release/flutter/eaeb4bdcae57b971d6dd7507a3b53e61676b8330/android-x64-release/symbols.zip>

## Uploading engine symbols to Crashlytics

On Android, Crashlytics only reports native crashes if you set up [NDK crash reporting](https://firebase.google.com/docs/crashlytics/android/get-started-ndk), which the FlutterFire plugin doesn’t add for you. Unzip each architecture’s `symbols.zip` into its own subdirectory of one folder, because every zip contains a file named `libflutter.so`. Then pass that folder to the Firebase CLI, which searches it recursively for `.so` files:

```
firebase crashlytics:symbols:upload --app=FIREBASE_APP_ID PATH/TO/SYMBOLS
```

On iOS, upload the engine’s dSYM zip in the dSYMs tab of the Crashlytics dashboard, or pass it to Crashlytics’ `upload-symbols` script, which accepts zip archives of dSYMs. See [Get readable crash reports](https://firebase.google.com/docs/crashlytics/ios/get-deobfuscated-reports) for how to run the script, or run it with `--help` for its options.

## Dart symbols for obfuscated builds

The engine symbols above cover Flutter’s C++ code. If you build with `--obfuscate` or `--split-debug-info`, your Dart code’s debug information is also removed from the app, and your crash reporting tool needs it to show readable Dart stack traces.

`--split-debug-info` writes that debug information to a directory, one file per architecture, such as `app.android-arm64.symbols` or `app.ios-arm64.symbols`:

```
shorebird release android --obfuscate --split-debug-info=build/symbols
```

If you pass `--obfuscate` without `--split-debug-info`, Shorebird writes the files to `build/shorebird/symbols`.

Upload the files in that directory by following your tool’s guide:

* [Sentry: Debug Symbols](https://docs.sentry.io/platforms/dart/guides/flutter/debug-symbols/)
* [Crashlytics: Get readable crash reports](https://firebase.google.com/docs/crashlytics/flutter/get-deobfuscated-reports)

iOS

On iOS, the symbol files are only usable by crash reporting tools for releases built with Flutter 3.47.5 or newer, the default Flutter version since Shorebird 1.6.123. What counts is the Flutter version the release was built with, so a release that pins an older version with `--flutter-version` still gets the old files. With those, tools such as `sentry-cli` report finding no debug information files, even though the upload command succeeds. Release again with Flutter 3.47.5 or newer to get readable Dart stack traces on iOS.

If `shorebird release ios` (1.6.135 and newer) prints this warning, your release’s Dart frames will have no function names, even though uploading the symbol file succeeds:

```
build/symbols/app.ios-arm64.symbols has no Dart debug info (no __debug_info section), so Dart frames in this release's crash reports will have no function names.

The likely cause is Xcode 27's linker with a Flutter version that doesn't support it yet. To get Dart symbols, run `flutter clean` and build the release with Xcode 26. https://github.com/shorebirdtech/shorebird/issues/4270 tracks the fix.
```

The release that printed the warning can’t get Dart symbols, and it’s already active, so `shorebird release ios` refuses the same version again. Bump your version number, run `flutter clean`, and release with Xcode 26.

`shorebird patch` accepts the same flags, and an obfuscated release’s patches are obfuscated too. When a patch writes to the same directory as an earlier build, whether the default or a path you pass again, it replaces that build’s files, because the file names are the same. Upload or copy a release’s files before you run the next build. For patches, pass `--split-debug-info` with a directory of their own; see [Reading crashes from patches](#reading-crashes-from-patches).

## Reading crashes from patches

### iOS

If you build with `--obfuscate` or `--split-debug-info` and a crash happens in code that a patch changed, your crash reporting tool can’t show the correct names for those Dart frames, even when you upload every symbol file. On iOS, code a patch changes runs in Shorebird’s interpreter rather than in your release’s binary, and crash reporting tools only match symbols to the binary your release shipped. Frames in code the patch didn’t change still run the release’s code, so they symbolicate as usual. This applies to both Sentry and Crashlytics.

Save each patch’s Dart symbol file: it describes the code the patch changed, which the release’s files don’t. Pass `--split-debug-info` to `shorebird patch ios`. Without it, a patch that isn’t obfuscated writes no symbol file. An obfuscated patch writes one to `build/shorebird/symbols`, but it describes a build step whose output doesn’t ship, so don’t upload or keep it as the patch’s symbols. The file is named `app.ios-arm64.symbols`, like the release’s, so give each patch its own directory, or the next build replaces it:

```
shorebird patch ios --release-version=1.0.0+1 --track=stable --split-debug-info=build/symbols/patch-2
```

Keep each release’s and each patch’s directory for as long as that release is installed. If you need a crash in patched code decoded, reach out on [Discord](https://discord.gg/shorebird).

### Android

If you build with `--obfuscate` or `--split-debug-info`, keep the symbol files from every `shorebird patch android` run as well as the release’s. On Android, a patch replaces your app’s compiled Dart code, so Dart frames in a patched app come from the patch’s code, not the release’s. Pass `--split-debug-info` to each patch, with its own directory:

```
shorebird patch android --release-version=1.0.0+1 --track=stable --split-debug-info=build/symbols/patch-2
```

To read a crash from a patched app, run `flutter symbolize` on the raw Dart stack trace with the patch’s symbol file for your device’s architecture, such as `app.android-arm64.symbols`. See `flutter symbolize --help` for how to pass the trace and the file.

Sentry matches symbols to each crash by build ID, so upload the patch’s symbol files after every `shorebird patch android`, the same as after a release.

Crashlytics can’t use a patch’s symbols with `firebase_crashlytics` 5.2.0 and newer. The plugin reports the build ID of the `libapp.so` in your APK, which is the release’s, so Crashlytics decodes patch frames with the release’s symbols.
