Learn
Choose the correct handling path
Decide whether a failure belongs in a typed result, local recovery, ordinary propagation, or the application-level error pipeline.
Choose the correct handling path
Ark Error Manager is not the first destination for every failure. Most application code should continue to use normal return values, typed failure results, try/catch, and ordinary error propagation. The manager coordinates the smaller set of incidents that reach an application boundary or explicitly require an application-wide decision.
Classify the outcome before choosing an API
| Situation | Correct path | Why |
|---|---|---|
| Form validation rejects input | Typed validation state | The application expects this outcome. |
| A search returns no items | Empty domain result | Nothing exceptional occurred. |
| Optional cache read fails but remote loading succeeds | Recover locally; optionally record bounded diagnostics | The operation has a valid fallback. |
| Repository cannot complete and its caller owns retry policy | Throw or return a typed failure to the caller | The decision still belongs to a local boundary. |
| Unhandled asynchronous error escapes the application task | Root Zone | The error has reached the host boundary. |
| Flutter framework callback reports a build or paint failure | FlutterError.onError binding | Flutter already owns the capture point. |
| Child Isolate terminates with an unhandled error | Isolate error-port binding | Zones do not cross Isolate boundaries. |
| Integration code must consume an exception and return a fallback, but reporting or recovery is still required | One deliberate handleError or captureError call | Normal propagation has been intentionally stopped. |
A local catch has three honest outcomes
try {
return await client.load();
} catch (error, stackTrace) {
// Choose exactly one primary path:
// 1. recover and return a normal result;
// 2. transform or rethrow for the caller;
// 3. submit once because this boundary consumes the error.
}
Recover locally
Return a fallback only when the domain accepts it as a valid result. Do not report the same failure globally merely because a catch block ran.
Preserve propagation
Use rethrow when a caller or root boundary still owns the decision. This retains the original stack trace and avoids a second reporting path.
Submit deliberately
Call handleError when the local boundary must wait until reporting, presentation, and recovery have completed. Call captureError when queueing the incident is enough. After submitting, do not rethrow into another configured boundary unless the application has an explicit deduplication identity and policy.
One failure, one application-level submission
Duplicate incidents commonly appear when:
- a feature catches and submits an error;
- the feature rethrows it;
- a Zone or Flutter hook captures it again;
- a preserved previous Flutter handler sends it to another crash reporter.
Choose one owner for remote reporting. If a previous handler must remain active, decide which system reports externally and how both systems correlate or deduplicate the incident.
Where ErrorManager is available
The application Bootstrap or Composition Root owns the manager and passes callbacks only to infrastructure bindings that genuinely need them. UseCases, Presenters, repositories, DataSources, and widgets keep their existing error contracts. They do not resolve a global manager.
The handled-error recipe is therefore an exception for a specific integration boundary, not a template to paste around every method.
Continue from here
- Read Ownership and boundaries to place the manager.
- Read Ordered processing pipeline to understand what happens after submission.
- Build the pure-Dart error boundary or Flutter root integration.
- Review Security and privacy before connecting a remote
ErrorReporter.