
Demangles and rewrites raw crash stack traces client-side for Crashlytics, Sentry, and Bugsnag, producing readable function names and source file:line locations with one-call setup.
Readable Kotlin crash stack traces for Firebase Crashlytics, Sentry, and Bugsnag.
Kotlin Multiplatform apps crash in shared Kotlin code on iOS, but the reports shown in
major crash-reporting vendors are unreadable — raw kfun:-mangled symbols, no source
file/line. KotlinTrace fixes the readability layer client-side, with no backend:
one call at startup, and the same crash shows demangled function names and
(File.kt:line) locations.
Works with the crash reporter you already use — Firebase Crashlytics, Sentry, or Bugsnag. No new backend, no vendor lock-in.
Before (what vendors normally receive):
IllegalStateException: Uncaught Kotlin exception
at 0 iosApp 0x1020e88e7 kfun:kotlin.Exception#<init>(kotlin/String?;kotlin.Throwable?){} + 119
at 1 iosApp 0x1020d3273 kfun:dev.kotlintrace.sample.CrashBot#crash(){}kotlin.Throwable + 159
... and 38 more stack frames
After (with KotlinTrace):
IllegalStateException: Uncaught Kotlin exception
at CrashBot.crash (CrashBot.kt:33)
at crashOnBackgroundThread$crashTrampoline (MainKt.kt:21)
KotlinTrace is iOS-only (Kotlin/Native). Add one dependency per vendor you use, call one function at startup — done.
// shared module build.gradle.kts — commonMain.dependencies
implementation("io.github.mahdimerhi:kotlintrace-crashlytics:0.1.0") // Firebase Crashlytics
implementation("io.github.mahdimerhi:kotlintrace-sentry:0.1.0") // Sentry
implementation("io.github.mahdimerhi:kotlintrace-bugsnag:0.1.0") // BugsnagAdd only the modules whose SDKs you already link. All three work side by side if you use multiple reporters.
Install the hook before any Kotlin code can crash — the earlier, the better. One call is enough; repeated calls and multi-adapter installs merge safely.
// Shared Kotlin, e.g. called from your SwiftUI App.init()
fun setupCrashReporting() {
KotlinTrace.installCrashlytics() // or installSentry() / installBugsnag()
}From Swift, call it through a tiny wrapper in your shared module:
@main
struct MyApp: App {
init() {
setupCrashReporting() // → KotlinTrace.installCrashlytics()
}
}Crash the app in shared Kotlin code (uncaught exception), relaunch, and check your
Crashlytics / Sentry / Bugsnag dashboard: the event now shows demangled Kotlin
functions with (File.kt:line).
KotlinTrace.installSentry(
demangle = true, // default: true
includeKotlinSourceLocation = true, // default: true
)
// same parameters on installCrashlytics() and installBugsnag()(File.kt:line) resolves via the K/N runtime's DWARF capabilities on device
and simulator. Release builds without debug info fall back to function names only.| Module | Entry point | Vendor SDK |
|---|---|---|
:kotlintrace-crashlytics |
KotlinTrace.installCrashlytics() |
Firebase Crashlytics |
:kotlintrace-sentry |
KotlinTrace.installSentry() |
sentry-cocoa |
:kotlintrace-bugsnag |
KotlinTrace.installBugsnag() |
bugsnag-cocoa |
The core engine (:kotlintrace) holds the uncaught-exception hook + demangler; the
adapters wire the vendors. The core's low-level API (KotlinTrace.install(...) +
KotlinTraceOptions) is there if you want to write your own sink — most apps just
use the install*() extensions above.
options.enableCrashHandler = false)Apache-2.0 — see LICENSE. Copyright (c) 2026 Mahdi Merhi.
Readable Kotlin crash stack traces for Firebase Crashlytics, Sentry, and Bugsnag.
Kotlin Multiplatform apps crash in shared Kotlin code on iOS, but the reports shown in
major crash-reporting vendors are unreadable — raw kfun:-mangled symbols, no source
file/line. KotlinTrace fixes the readability layer client-side, with no backend:
one call at startup, and the same crash shows demangled function names and
(File.kt:line) locations.
Works with the crash reporter you already use — Firebase Crashlytics, Sentry, or Bugsnag. No new backend, no vendor lock-in.
Before (what vendors normally receive):
IllegalStateException: Uncaught Kotlin exception
at 0 iosApp 0x1020e88e7 kfun:kotlin.Exception#<init>(kotlin/String?;kotlin.Throwable?){} + 119
at 1 iosApp 0x1020d3273 kfun:dev.kotlintrace.sample.CrashBot#crash(){}kotlin.Throwable + 159
... and 38 more stack frames
After (with KotlinTrace):
IllegalStateException: Uncaught Kotlin exception
at CrashBot.crash (CrashBot.kt:33)
at crashOnBackgroundThread$crashTrampoline (MainKt.kt:21)
KotlinTrace is iOS-only (Kotlin/Native). Add one dependency per vendor you use, call one function at startup — done.
// shared module build.gradle.kts — commonMain.dependencies
implementation("io.github.mahdimerhi:kotlintrace-crashlytics:0.1.0") // Firebase Crashlytics
implementation("io.github.mahdimerhi:kotlintrace-sentry:0.1.0") // Sentry
implementation("io.github.mahdimerhi:kotlintrace-bugsnag:0.1.0") // BugsnagAdd only the modules whose SDKs you already link. All three work side by side if you use multiple reporters.
Install the hook before any Kotlin code can crash — the earlier, the better. One call is enough; repeated calls and multi-adapter installs merge safely.
// Shared Kotlin, e.g. called from your SwiftUI App.init()
fun setupCrashReporting() {
KotlinTrace.installCrashlytics() // or installSentry() / installBugsnag()
}From Swift, call it through a tiny wrapper in your shared module:
@main
struct MyApp: App {
init() {
setupCrashReporting() // → KotlinTrace.installCrashlytics()
}
}Crash the app in shared Kotlin code (uncaught exception), relaunch, and check your
Crashlytics / Sentry / Bugsnag dashboard: the event now shows demangled Kotlin
functions with (File.kt:line).
KotlinTrace.installSentry(
demangle = true, // default: true
includeKotlinSourceLocation = true, // default: true
)
// same parameters on installCrashlytics() and installBugsnag()(File.kt:line) resolves via the K/N runtime's DWARF capabilities on device
and simulator. Release builds without debug info fall back to function names only.| Module | Entry point | Vendor SDK |
|---|---|---|
:kotlintrace-crashlytics |
KotlinTrace.installCrashlytics() |
Firebase Crashlytics |
:kotlintrace-sentry |
KotlinTrace.installSentry() |
sentry-cocoa |
:kotlintrace-bugsnag |
KotlinTrace.installBugsnag() |
bugsnag-cocoa |
The core engine (:kotlintrace) holds the uncaught-exception hook + demangler; the
adapters wire the vendors. The core's low-level API (KotlinTrace.install(...) +
KotlinTraceOptions) is there if you want to write your own sink — most apps just
use the install*() extensions above.
options.enableCrashHandler = false)Apache-2.0 — see LICENSE. Copyright (c) 2026 Mahdi Merhi.