-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Handle errors once #179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle errors once #179
Changes from 1 commit
6f4c53e
aeb880e
9114524
d269146
0cbb0e6
9ffd194
bf126d6
e5a06e4
bde62c8
0aca677
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# Handle Errors Once | ||
|
||
When a function receives an error from a caller, | ||
it can handle it in a variety of different ways. | ||
These include, but not are limited to: | ||
|
||
- returning the error | ||
- [wrapping the error](error-wrap.md) and returning that | ||
- logging the error and returning nil | ||
- logging the error and returning a different error | ||
- logging the error and continuing running | ||
- matching the error with `errors.Is` or `errors.As` | ||
and then handling the two error branches differently | ||
|
||
In general, a function should handle an error only once. | ||
It should not, for example, log the error and then return it again | ||
because its callers will likely handle the error too. | ||
|
||
As demonstrative examples, consider the following cases: | ||
|
||
<table> | ||
<thead><tr><th>Description</th><th>Code</th></tr></thead> | ||
<tbody> | ||
<tr><td> | ||
|
||
**Bad**: Log the error and return it | ||
|
||
Callers will likely handle the error as well--likely logging it before doing so | ||
causing a lot of noise in the application logs. | ||
|
||
</td><td> | ||
|
||
```go | ||
u, err := getUser(id) | ||
if err != nil { | ||
// BAD: See description | ||
log.Printf("Could not get user %q: %v", id, err) | ||
return err | ||
Comment on lines
+41
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (tangentially related) I often see this with structured logging when you want id as a separate field, but returning an error forces you to flatten the id into the error string, or you have to add an additional return of what fields to log (making it much more complex) I've been meaning to solve it for a while, and think it can be solved relatively simply, |
||
} | ||
``` | ||
|
||
</td></tr> | ||
<tr><td> | ||
|
||
**Good**: Wrap the error and return it | ||
|
||
Callers will be able to handle the error, | ||
matching with `errors.Is` or `errors.As` for specific errors. | ||
|
||
</td><td> | ||
|
||
```go | ||
u, err := getUser(id) | ||
if err != nil { | ||
return fmt.Errorf("get user %q: %w", id, err) | ||
} | ||
``` | ||
|
||
</td></tr> | ||
<tr><td> | ||
|
||
**Good**: Log the error and degrade gracefully | ||
|
||
If retrieving the value is not strictly necessary, | ||
we can provide a degraded but unbroken experience | ||
by using a fallback value. | ||
|
||
</td><td> | ||
|
||
```go | ||
u, err := getUser(id) | ||
if err != nil { | ||
log.Printf("Could not get user %q: %v", id, err) | ||
u = nil | ||
} | ||
|
||
``` | ||
|
||
</td></tr> | ||
<tr><td> | ||
|
||
**Good**: Match the error and degrade gracefully | ||
|
||
If it's expected for retrieval to fail in specific cases, | ||
match on that error case and degrade gracefully. | ||
|
||
For all other cases, wrap the error and return it. | ||
Callers will be able to add their own special handling | ||
if necessary. | ||
|
||
</td><td> | ||
|
||
```go | ||
|
||
u, err := getUser(id) | ||
if err != nil { | ||
if !errors.Is(err, ErrNotFound) { | ||
return fmt.Errorf("get user %q: %w", id, err) | ||
} | ||
log.Printf("User %q does not exist", id) | ||
u = nil | ||
} | ||
``` | ||
|
||
</td></tr> | ||
</tbody></table> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how does a function receive an error from a caller or am I being stupid here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦 oops. yeah, callee.