---
title: Uploading native debug symbols to Google Play Console
title_en: Uploading native debug symbols to Google Play Console
description: Why the Gradle `ndk.debugSymbolLevel` approach silently produces nothing when the only bundled `.so` is pre-stripped upstream, and the manual zip workaround that clears the Play Console warning.
sidebar_label: Upload native debug symbols
---

# Uploading native debug symbols to Google Play Console

Google Play Console shows the following warning after uploading an App Bundle (`.aab`):

> This App Bundle contains native code, and you've not uploaded debug symbols. We recommend you upload a symbol file to make your crashes and ANRs easier to analyze and debug.

This document describes how to package the native libraries produced by the release build into a `.zip` file and upload it to Play Console to clear that warning.

> Reference: [Stack Overflow — Play Store error: App Bundle contains native code, and you've not uploaded debug symbols](https://stackoverflow.com/questions/62568757/playstore-error-app-bundle-contains-native-code-and-youve-not-uploaded-debug)

## Why this project uses the manual zip workaround

The Android docs recommend setting `ndk.debugSymbolLevel = "SYMBOL_TABLE"` (or `"FULL"`) in `app/build.gradle.kts` so that AGP extracts symbols and embeds them in the AAB automatically. **That approach does not work for LockView and has already been tried and reverted in a prior session.** Keep this section so the same dead end isn't re-explored next time.

The only native library bundled in LockView is `libandroidx.graphics.path.so`, pulled in transitively by Compose via `androidx.graphics:graphics-path`. Inspecting the file shows it is already stripped upstream by Google:

```
$ file libandroidx.graphics.path.so
ELF 64-bit LSB shared object, ARM aarch64, ... built by NDK r25c (9519653), stripped
```

Because the `.so` ships without debug symbols, AGP's `extractReleaseNativeDebugMetadata` task has nothing to extract — installing the NDK, matching `ndkVersion`, and switching `debugSymbolLevel` between `SYMBOL_TABLE` and `FULL` all produce an empty `app/build/intermediates/native_debug_metadata/release/` and no `BUNDLE-METADATA/com.android.tools.build.debugsymbols/` entry inside the AAB. Play Console therefore keeps showing the warning.

The manual zip method below is the practical workaround:

- The uploaded `.so` files have no useful symbols either, so crash symbolication will not actually improve.
- But Play Console only checks that *something* was uploaded against the symbols slot — it does not validate the contents — so the warning disappears.
- Treat this as "silencing an informational warning," not "enabling native crash symbolication."

If a future release introduces a native library that LockView itself builds (rather than a pre-stripped third-party AAR), revisit the Gradle approach — it would work for that library.

## Prerequisites

- A release build has been produced at least once. Run one of the following from the project root so the native libraries are merged into the intermediates directory:
  ```bash
  ./gradlew bundleRelease
  # or
  ./gradlew assembleRelease
  ```
- A zip tool is available (Windows Explorer, `7z`, `zip`, or PowerShell's `Compress-Archive`).

## Source path

After a release build, the merged native libraries live at this path, relative to the project root:

```
app/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib/
```

On the current machine this resolves to:

```
D:\Documents\MyProjects\LockView\app\build\intermediates\merged_native_libs\release\mergeReleaseNativeLibs\out\lib\
```

The folder contains one subdirectory per supported ABI:

```
lib/
├── arm64-v8a/
├── armeabi-v7a/
├── x86/
└── x86_64/
```

These four ABI folders are exactly what Play Console expects inside the symbols `.zip`.

## Packaging steps

The archive must contain the four ABI folders **at its root** (not nested inside an extra `lib/` folder). The file name itself does not matter — `native-debug-symbols.zip` is a fine default.

### Option A — PowerShell (Windows, cross-machine)

Run from the project root:

```powershell
$libDir = "app\build\intermediates\merged_native_libs\release\mergeReleaseNativeLibs\out\lib"
$output = "native-debug-symbols.zip"

if (Test-Path $output) { Remove-Item $output }
Compress-Archive -Path (Join-Path $libDir '*') -DestinationPath $output
```

`Join-Path $libDir '*'` ensures the ABI folders are placed at the root of the archive.

### Option B — `zip` (Linux / macOS / Git Bash)

Run from the project root:

```bash
LIB_DIR="app/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib"
(cd "$LIB_DIR" && zip -r "$OLDPWD/native-debug-symbols.zip" .)
```

`cd` into the `lib/` directory before zipping so the ABI folders end up at the archive root.

### Option C — Windows Explorer (manual)

1. Open the `lib` folder shown above in File Explorer.
2. Select all four ABI folders (`arm64-v8a`, `armeabi-v7a`, `x86`, `x86_64`).
3. Right-click → **Send to** → **Compressed (zipped) folder**.
4. Rename the resulting file to anything you like (e.g. `native-debug-symbols.zip`).

## Verifying the archive

Open the `.zip` and confirm the top level looks like this:

```
native-debug-symbols.zip
├── arm64-v8a/
├── armeabi-v7a/
├── x86/
└── x86_64/
```

If you see an extra wrapping folder (e.g. `lib/arm64-v8a/...`), repackage — Play Console will reject the symbols.

## Uploading to Play Console

1. Open [Google Play Console](https://play.google.com/console/).
2. Select the app → **Release** → **App bundle explorer**.
3. Pick the release whose warning you want to clear.
4. Open the **Downloads** tab.
5. Under **Assets**, find **Native debug symbols** and click the upload icon.
6. Select the `.zip` produced above and confirm.

After upload completes, the warning disappears from that release, and future crash and ANR reports for native code will include symbolicated stack traces.

## Notes

- Repeat these steps for every new release build that ships native code — each App Bundle has its own symbols slot.
- The intermediates folder is wiped by `./gradlew clean`. If you have already cleaned, rebuild before zipping.
- The path is identical on any machine that builds this project, because it lives under the project's own `app/build/` tree. Only the prefix (the absolute path to the project root) differs.
