Skip to content

Uploading Symbols

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.

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

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

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 or higher in the organization that owns the app. The console doesn’t link to Android engine symbols. Use the download URLs 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)

Section titled “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.

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.

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

On Android, Crashlytics only reports native crashes if you set up NDK crash reporting, 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 for how to run the script, or run it with --help for its options.

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:

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.

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.

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.