4.1 KiB
Error Reporting
First we need to make the distinction between expected runtime errors and
exceptions. Unfortunately both are represented with the Error
class, but
they're conceptually different. Exceptions are fatal, errors are not.
The story around exceptions is simpler so let's start there.
Exceptions
An exception is an unexpected, fatal problem in the app itself. For example, our
old friend undefined is not a function
. This is a problem with the code itself
which cannot be resolved at runtime. Our only option is to quit the app and
relaunch.
We handle uncaught exceptions by registering a global listener. We report the exception to Central, tell the user that an unrecoverable error happened, and then quit and relaunch. End of story.
Errors
Errors are a bit more involved. They are anything that can go wrong in the standard usage of the app. For example, if the internet's down or a git repository is in a funny state, we're gonna get some errors.
Our error reporting flows through the Dispatcher
like most everything in the
app. postError
calls the registered
error handlers,
starting with the most recently registered. The error handlers have the chance
to pass the error through untouched, return a different or more specific error,
or swallow the error entirely.
Error handlers must have the following type:
export async function myCoolErrorHandler(
error: Error,
dispatcher: Dispatcher
): Promise<Error | null> {
// code goes here
}
If an error passes through all the registered error handlers, the final error
handler will call Dispatcher#presentError
.
That will present the generic error dialog to the user.
+------------------------+
| |
| Dispatcher#postError |
| |
+------------------------+
|
|
+------------------+ +--------------------+
| | | |
| error handlers |-----| do something else |
| | | |
+------------------+ +--------------------+
|
|
+-------------------------+
| |
| Dispatcher#presentError |
| |
+-------------------------+
Error
subclasses
We define some Error
subclasses that are used in the codebase use to provide
more context to error handlers:
GitError
- wraps a rawError
raise bydugite
with additional git information.ErrorWithMetadata
- wraps an existingError
with additional metadata.
In addition to the global information like the repository associated with the
error, ErrorWithMetadata
supports providing additional details:
- a
RetryAction
can be set to let the user retry the action that previously failed, allowing error handlers the ability to retry whatever action caused the error. - a
IGitErrorContext
can be set to add custom details about the Git operation that failed, so that error handlers have more context to provide the user about how to recover