Skip to content

Flutter

athar_errors sends Dart errors, native crashes and (if you turn them on) breadcrumbs, anonymized, to your app’s ingest host. It never interrupts the user.

Install

Not published yet

The package isn’t on pub.dev yet: it comes with your account, under the name below.

dependencies:
  athar_errors: ^0.1.0

On Android, the app needs the INTERNET permission.

Start it

void main() => ErrorReporting.runGuarded(() async {
      await ErrorReporting.init(const ErrorOptions(
        dsn: 'https://ik_…@<your ingest host>/<app id>', // shown once, with the app's key
        release: '4.2.0+420',
        environment: 'production',
        appVersion: '4.2.0',
        inAppPackages: ['my_app'], // your own packages: their frames group the issue
      ));
      runApp(const MyApp());
    });
  • runGuarded reports errors that escape to the zone.
  • init hooks FlutterError.onError and PlatformDispatcher.instance.onError. The handlers you had before still run right after it, so other tools and the debug console behave as before.

Native crashes

  • Android: an uncaught JVM exception is written to a file, and then the app’s previous handler runs, so the app dies exactly as before. Native (NDK) crashes and ANRs come from the system’s exit records (Android 11+). They carry no stack, so each groups as one issue per kind (NativeCrash, ApplicationNotResponding), with the signal in its message when Android gives one.
  • iOS: an uncaught NSException is written to a file. Signals and watchdog kills come from MetricKit’s crash reports (iOS 14+), which iOS delivers on a later launch.
  • Either way, the crash is sent on the next launch as fatal, with the version that crashed.

Everything else it can send

  • A caught error: ErrorReporting.captureError(e, stack).
  • Product events: ErrorReporting.track('checkout.done', {'items': 3}).
  • Your user: ErrorReporting.identify('your-user-id'). Use your own opaque id, never an email. It is kept across launches, and identify(null) forgets it.
  • Breadcrumbs are off by default. With breadcrumbs: true:
    • add ErrorReporting.navigatorObserver to navigatorObservers, and route names are recorded as templates (/orders/:id, with no query string);
    • ErrorReporting.addBreadcrumb('…') adds your own.

The user’s profile (optional)

With profilesUrl and a publishable profilesKey (pk_…) in ErrorOptions, identify('id', email: …, phone: '+…', attributes: {…}) also sets the user’s profile (the email and phone are hashed by the service on arrival), track also records the event there, and ErrorReporting.setConsent('push', true) records a channel choice. It is all queued (at most 200 items, 7 days) and follows the same opt-out. Without the two options nothing goes there.

What it collects

  • Collected: the error type, its message and stack frames (function, library, line), and the app version, build, release and environment. Also the OS and its major version, phone or tablet, and the language. Frames never carry local variables or arguments.
  • Scrubbed on the device first: emails, phone numbers and any run of 7+ digits, IP addresses, tokens and keys, UUIDs, query strings, and home directories. Each becomes a placeholder (<email>, <number>…). Add your own patterns with scrubPatterns; the built-in ones can’t be turned off.
  • Never collected: names, emails, device model or ids, advertising ids, location, IP addresses, or anything on screen or typed. No UI of its own, ever. The one exception is opt-in: with profilesUrl and profilesKey, what you pass to identify goes to the user’s profile.

What it never does

  • It never throws into your app. If the SDK itself fails, it turns itself off for the session.
  • Offline: at most 50 items for up to 7 days, oldest dropped first, each at most 64 KB. They are sent when the app comes back to the foreground, with backoff while the service is unreachable. The same error more than 10 times a minute is counted once on the device.
  • ErrorReporting.setEnabled(false) stops capture and deletes what is queued. It holds across launches.
  • Strict mode (requireConsent: true) captures nothing until ErrorReporting.grantConsent(); revokeConsent() deletes what’s queued.

Your privacy policy: a template

This app sends anonymous error reports to help us fix crashes. A report contains the error, the app and OS version, and the device type. It does not contain your name, email, account, location, IP address, or anything you typed. You can turn this off in Settings → [your switch]. Reports are kept for 30 days.

If you set profilesUrl and profilesKey, this template no longer covers you: say what you pass to identify (an email, a phone, attributes), and what the user’s profile is used for.