Error Tracking aggregates the errors reported by your Android application into issues, grouping occurrences that share a root cause so a crash affecting a thousand users is one entry rather than a thousand. Each issue carries a stack trace, the device, OS, and app version distribution it occurs across, and a link to the RUM session that produced it.

This page covers what the SDK reports, how to configure each error source, and how to make obfuscated release traces readable.

What is reported

With RUM enabled, the SDK collects four classes of error:

  • Crashes, uncaught exceptions that terminate the process, captured through the JVM's default uncaught exception handler.
  • ANRs, raised when the main thread is blocked long enough for the system to consider the application unresponsive.
  • Network errors, for requests that fail or return an error status on hosts instrumented by AtatusInterceptor.
  • Handled exceptions, reported explicitly through addError.

Every error is attributed to the view and session active when it occurred, so an issue resolves to the user journey that produced it rather than a stack trace in isolation.

Prerequisites

The SDK must be initialized and RUM enabled. See Android Monitoring Setup.

Report crashes

Crash reporting is enabled by default and requires no configuration beyond the base integration. Set it explicitly to document the intent, or to disable it where another handler owns the process:

copy
icon/buttons/copy
val configuration = Configuration.Builder(
    licenseKey = "<LICENSE_KEY>",
    env = "<ENV_NAME>",
    variant = "<APP_VARIANT_NAME>"
)
    .setCrashReportsEnabled(true)
    .build()

A crash is persisted to disk as the process terminates and uploaded on the next launch, since the SDK has no opportunity to complete a network request while the process is being torn down.

Report ANRs

Fatal ANRs, where the system terminates the application, are reported automatically. Non-fatal ANRs, where the main thread recovers after blocking, are disabled by default because their volume varies widely between applications:

copy
icon/buttons/copy
val rumConfiguration = RumConfiguration.Builder()
    .trackNonFatalAnrs(true)
    .build()
Note:

Non-fatal ANRs and long tasks share a cause: work scheduled on the main thread that belongs on a background dispatcher. If ANR volume is high, correlate it with long tasks to locate the blocking call sites.

Report handled exceptions

addError promotes an exception you caught into a RUM error event, so failures your application degrades gracefully around are still measurable:

copy
icon/buttons/copy
import com.atatus.android.rum.GlobalRumMonitor
import com.atatus.android.rum.RumErrorSource

try {
    processPayment(order)
} catch (e: PaymentException) {
    GlobalRumMonitor.get().addError(
        message = "Payment failed",
        source = RumErrorSource.SOURCE,
        throwable = e,
        attributes = mapOf("order.id" to order.id)
    )
}

Always pass the Throwable when one is available. Issue grouping is derived from the stack trace, so errors reported without one collapse on message alone and group far less precisely.

The source classifies where the error originated and determines how it is categorized in your dashboard:

Source Applies to
SOURCE Exceptions raised by your own application code.
NETWORK Failures originating from a network request.
WEBVIEW Errors propagated from embedded web content.
CONSOLE Errors captured from console output.

Deobfuscate release stack traces

R8 and ProGuard rename classes and methods during release builds, leaving production stack traces unreadable. Uploading the mapping file lets Atatus restore the original symbols server-side, so the obfuscated build you ship stays diagnosable.

After a release build, the mapping file is written to:

copy
icon/buttons/copy
app/build/outputs/mapping/release/mapping.txt

Upload it from your RUM application settings in the Atatus dashboard, matching the version name and build number of the release it was produced for.

Warning:

Upload a mapping file for every release you ship. R8 generates a distinct mapping per build, so a trace from version 2.1 cannot be resolved against the mapping from 2.0, and any release without an upload remains obfuscated permanently.

Preserve line number metadata so deobfuscated traces resolve to source lines rather than method names alone:

copy
icon/buttons/copy
-keepattributes SourceFile,LineNumberTable

Filter out noisy errors

Some failures are expected and carry no diagnostic value, such as requests cancelled when a user navigates away mid-flight. Discard them with an error event mapper, which runs before serialization:

copy
icon/buttons/copy
val rumConfiguration = RumConfiguration.Builder()
    .setErrorEventMapper { event ->
        if (event.error.message?.contains("Canceled") == true) null else event
    }
    .build()

Returning null drops the event. Crash events are exempt from this filter, since suppressing them would conceal fatal failures.

Next steps