# Debug files & native symbolication > https://docs.errora.org/en/products/debug-files A release build of a mobile or native app doesn't ship its names. An iOS or Android NDK crash reports instruction addresses; an R8-minified Android app reports `a.b.c.d`. **Debug files** are what turn those back into your code — the native counterpart of [source maps](/en/products/source-maps). Errora applies them at ingest, **before grouping**, so issues group on real function names that stay stable across builds. ## What gets resolved - **Native frames** (iOS crashes, Android NDK) → function name, file and line. C++ and Swift names are demangled, and inlined frames are expanded into frames of their own. - **Java/Kotlin frames** from an R8/ProGuard build → original class, method, file and line, with inlined methods expanded. Obfuscated **exception class names** are restored too. - **Profiles**: iOS profile frames are symbolicated with dSYMs, and Android profile method names are remapped through the ProGuard mapping — see [Profiling](/en/products/profiling). ## Supported formats | Format | Platform | Where it comes from | | --- | --- | --- | | dSYM (Mach-O DWARF) | iOS, macOS | Xcode, with *Debug Information Format = DWARF with dSYM File* | | ELF (`.so`, unstripped) | Android NDK, Linux | The NDK build's intermediate, unstripped libraries | | Breakpad (`.sym`) | Any native | `dump_syms` | | PDB | Windows | The MSVC linker | | ProGuard/R8 `mapping.txt` | Android (Java/Kotlin) | `app/build/outputs/mapping//mapping.txt` | ## How a file is matched Every file is identified by a **debug id**, never by name or release: - **Native files** carry one inside the binary (the Mach-O UUID, the ELF build id, the PDB signature, the Breakpad module id). The SDK reports the debug id of every image loaded in the crashed process, and each frame's address is resolved against the file with that id. - **ProGuard mappings** are matched by a **UUID** that the Gradle plugin stamps into the app and the SDK reports with each event. So the file must come from the **exact build** that crashed. Rebuilding the same commit gives new debug ids, and the old crashes won't match the new files. ## Uploading Use the official Sentry tooling, unchanged — Errora implements the upload endpoints `sentry-cli` talks to. Point it at Errora with four settings: | Setting | Value | | --- | --- | | `SENTRY_URL` | `https://` | | `SENTRY_AUTH_TOKEN` | An Errora [personal access token](/en/account/tokens) (`errora_pat_…`) | | `SENTRY_ORG` | The organization slug (from `/organizations//…`) | | `SENTRY_PROJECT` | The project slug | The same values go in a `.sentryclirc` (`url`, `org`, `project`, `token`) or the Gradle `sentry {}` block. The token's owner needs the **member** role or higher in the organization. ### sentry-cli ```bash # dSYMs, ELF, Breakpad, PDB — point it at a file or a directory sentry-cli debug-files upload path/to/build/ # ProGuard/R8 mapping sentry-cli upload-proguard --uuid app/build/outputs/mapping/release/mapping.txt ``` ### Per platform - **Android** — the Sentry Android Gradle plugin uploads the mapping and NDK symbols on every release build and stamps the ProGuard UUID for you. See [Android](/en/platforms/android). - **iOS** — an Xcode *Run Script* build phase running `sentry-cli debug-files upload`, or fastlane's `sentry_debug_files_upload`. See [iOS](/en/platforms/apple-ios). ### From the browser **Settings → Debug files** uploads a file by hand, and lists, downloads and deletes what's there. Native files carry their own debug id. For a ProGuard mapping, fill in the optional **debug id** field with the UUID your app reports, so the mapping matches its events. ## Reading an issue with missing files A frame whose debug file is missing is **kept**, not dropped, and flagged as unsymbolicated. The issue page lists the **missing debug ids** — with the image or mapping each belongs to — so you know exactly which file to upload. Debug files only apply to events that arrive **after** the upload: upload the file, then wait for the next occurrence. ## Self-hosting | Setting | Default | Meaning | | --- | --- | --- | | `DEBUG_FILES_STORAGE` | `filesystem` | `filesystem` keeps files on disk under `DEBUG_FILES_ROOT`; `s3` keeps them in an S3-compatible bucket | | `DEBUG_FILES_ROOT` | `backend/var/debug-files` | Where files live with `filesystem` storage. Must be a **shared volume** when ingest workers run on several hosts — or use `s3` | | `DEBUG_FILES_S3_BUCKET`, `DEBUG_FILES_S3_ENDPOINT_URL`, `DEBUG_FILES_S3_REGION` | — | The bucket for `s3` storage (any S3-compatible service) | | `DEBUG_FILES_S3_ACCESS_KEY`, `DEBUG_FILES_S3_SECRET_KEY` | — | Its credentials — keep them in a secret | | `DEBUG_FILES_S3_PREFIX` | `debug-files` | Key prefix inside the bucket | | `DEBUG_FILES_CACHE_DIR` | system temp dir | With `s3`, each worker keeps the files it symbolicates with here (no volume needed) | | `DEBUG_FILES_CACHE_MAX_BYTES` | 2 GB | Size of that cache; least recently used files go first | | `DEBUG_FILE_UPLOAD_MAX_BYTES` | 500 MB | Cap on a single browser upload | With `s3` the workers are stateless: nothing needs to be mounted, and a replaced pod re-downloads what it needs on first use. Upload chunks left behind by `sentry-cli` are trimmed nightly. ## Limitations - **Minidumps** (Breakpad, Crashpad, sentry-native) are stack-walked on the server, as Sentry does, with rust-minidump's `minidump-stackwalk` (in the Docker image; set `MINIDUMP_STACKWALK` for a custom build). Upload the binaries' debug files so the walk can use their unwind information; without them it falls back to frame pointers and stack scanning, and each frame shows how it was found (`context`, `cfi`, `fp`, `scan`). - **Swift names** are demangled with the Swift runtime library (`libswiftCore`), which the Docker image includes; a self-built image without it shows Swift symbols mangled (`$s7MyApp…`). C++ is always demangled. - **Frames without a line number** (Android profiles) are resolved the way Sentry does: by the method's parameter types when the frame carries its signature, otherwise by name — so an obfuscated name that maps to several methods with the same parameters stays as-is. ## Troubleshooting | Symptom | Cause / fix | | --- | --- | | Frames still show addresses / `a.b.c` | The file for that exact build wasn't uploaded — compare the missing debug id on the issue with **Debug files** | | A file is listed but still doesn't match | It's from a different build (same version, rebuilt) — upload from the job that produced the shipped binary | | NDK frames unresolved | You uploaded stripped `.so` files — upload the unstripped ones from the build intermediates | | ProGuard mapping uploaded by hand never matches | Its debug id isn't the UUID the app reports | | `sentry-cli` uploads to sentry.io | `SENTRY_URL` / `url` isn't set | | `403` on upload | The token's owner isn't a member of that organization |