github-desktop/docs/technical/error-reporting.md
2018-10-25 12:09:41 -03:00

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 raw Error raise by dugite with additional git information.
  • ErrorWithMetadata - wraps an existing Error 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