Error Handling Best Practices
Ten strategies for effective error tracking — initWithZone, stack traces, context, breadcrumbs, crash detection, and what to report.
Best practices for implementing effective error tracking in your application. Follow these 10 strategies to get the most out of AbrovaTrace.
1. Use initWithZone() for Comprehensive Error Catching#
Catches all error types including Dart exceptions, async errors, Future rejections, and platform exceptions.
✓ Recommended
void main() async {
await AbrovaTrace.initWithZone(
AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
release: '1.0.0',
captureUncaughtErrors: true,
),
() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
},
);
}✗ Basic (misses async errors)
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// This only catches some errors
await AbrovaTrace.init(
AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: 'production',
),
);
runApp(MyApp());
}2. Always Include Stack Traces#
When manually capturing exceptions, always include the stack trace parameter for crucial debugging context.
try {
await riskyOperation();
} catch (e, stackTrace) {
await AbrovaTrace.captureException(
e,
stackTrace: stackTrace, // Always include this!
message: 'Failed to complete operation',
);
}3. Add Meaningful Context#
Include relevant data using the extra parameter to understand the circumstances.
try {
await processPayment(orderId, amount);
} catch (e, stackTrace) {
await AbrovaTrace.captureException(
e,
stackTrace: stackTrace,
message: 'Payment processing failed',
extra: {
'order_id': orderId,
'amount': amount,
'payment_method': paymentMethod,
'user_tier': userTier,
},
);
}4. Combine Errors with Breadcrumbs#
Track user actions leading up to an error for a complete timeline.
// Track user actions
AbrovaTrace.addBreadcrumb(
'User opened checkout screen',
type: BreadcrumbType.navigation,
);
AbrovaTrace.addBreadcrumb(
'User selected credit card payment',
type: BreadcrumbType.user,
data: {'payment_type': 'credit_card'},
);
try {
await submitOrder();
} catch (e, stackTrace) {
// Error will include all breadcrumbs above
await AbrovaTrace.captureException(
e,
stackTrace: stackTrace,
message: 'Order submission failed',
);
}5. Implement Crash Detection#
Native crash capture is automatic on iOS and Android: the Flutter SDK bundles the native Abrova Trace SDKs and starts them during init() / initWithZone(). No extra code is needed.
How it works: on iOS, PLCrashReporter saves signal and Mach exception crashes to disk; on Android, an uncaught exception handler plus ApplicationExitInfo (API 30+) record crashes and ANRs. The report is sent on the next app launch and appears as an issue. Desktop and web have no native crash capture.
AbrovaTrace.close() is not needed for crash detection. It flushes pending logs and tears the SDK down, so call it only when the app is really shutting down.
6. Use Environment-Specific Configuration#
void main() async {
const environment = String.fromEnvironment(
'ENV', defaultValue: 'development',
);
await AbrovaTrace.initWithZone(
AbrovaTraceConfig(
apiKey: 'ab_live_your_api_key',
environment: environment,
release: '1.0.0',
captureUncaughtErrors: environment == 'production',
enabled: environment != 'development',
),
() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
},
);
}7. Manage Error Status in Dashboard#
- Open: New errors that need investigation
- In Progress: Errors you're actively working on
- Resolved: Fixed errors (reopen if they recur)
- Ignored: Known issues you're choosing not to fix
- Archived: Historical errors to keep but not see in active list
8. Handle Async Errors Properly#
// Catching Future errors
Future<void> loadData() async {
try {
final data = await fetchFromAPI();
processData(data);
} catch (e, stackTrace) {
await AbrovaTrace.captureException(
e, stackTrace: stackTrace,
message: 'Failed to load data',
);
}
}
// Handling Stream errors
void listenToStream() {
myStream.listen(
(data) => processData(data),
onError: (e, stackTrace) async {
await AbrovaTrace.captureException(
e, stackTrace: stackTrace,
message: 'Stream error occurred',
);
},
);
}9. Don't Over-Report#
- Expected validation errors
- User cancellations
- Network timeouts in offline mode
- Permission denials
- Unexpected exceptions (null ref, index out of bounds)
- API errors (server 500, malformed responses)
- Data corruption
- Native crashes
10. Test Your Error Tracking#
Before deploying to production, verify error tracking works (the event appears under Abrova Trace → Issues):
// Add a test button in debug builds
if (kDebugMode) {
ElevatedButton(
onPressed: () async {
try {
throw Exception('Test error from debug menu');
} catch (e, stackTrace) {
await AbrovaTrace.captureException(
e,
stackTrace: stackTrace,
message: 'Test error',
);
}
},
child: Text('Test Error Tracking'),
);
}The SDK drops an identical error (same message and platform) sent again within one minute, so change the message if you test several times in a row.
Summary Checklist#
- ✓Use initWithZone() for comprehensive error catching
- ✓Always include stack traces in manual captures
- ✓Add meaningful context with the extra parameter
- ✓Combine errors with breadcrumbs for user journey tracking
- ✓Test on a real iOS/Android build so native crashes are captured
- ✓Use environment-specific configurations
- ✓Manage error status effectively in the dashboard
- ✓Handle async errors properly
- ✓Only report actionable errors
- ✓Test your error tracking before production