snapshot of https://github.com/uber-go/guide.git (master, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
79
src/error-wrap.md
Normal file
79
src/error-wrap.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Error Wrapping
|
||||
|
||||
There are three main options for propagating errors if a call fails:
|
||||
|
||||
- return the original error as-is
|
||||
- add context with `fmt.Errorf` and the `%w` verb
|
||||
- add context with `fmt.Errorf` and the `%v` verb
|
||||
|
||||
Return the original error as-is if there is no additional context to add.
|
||||
This maintains the original error type and message.
|
||||
This is well suited for cases when the underlying error message
|
||||
has sufficient information to track down where it came from.
|
||||
|
||||
Otherwise, add context to the error message where possible
|
||||
so that instead of a vague error such as "connection refused",
|
||||
you get more useful errors such as "call service foo: connection refused".
|
||||
|
||||
Use `fmt.Errorf` to add context to your errors,
|
||||
picking between the `%w` or `%v` verbs
|
||||
based on whether the caller should be able to
|
||||
match and extract the underlying cause.
|
||||
|
||||
- Use `%w` if the caller should have access to the underlying error.
|
||||
This is a good default for most wrapped errors,
|
||||
but be aware that callers may begin to rely on this behavior.
|
||||
So for cases where the wrapped error is a known `var` or type,
|
||||
document and test it as part of your function's contract.
|
||||
- Use `%v` to obfuscate the underlying error.
|
||||
Callers will be unable to match it,
|
||||
but you can switch to `%w` in the future if needed.
|
||||
|
||||
When adding context to returned errors, keep the context succinct by avoiding
|
||||
phrases like "failed to", which state the obvious and pile up as the error
|
||||
percolates up through the stack:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>
|
||||
|
||||
```go
|
||||
s, err := store.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to create new store: %w", err)
|
||||
}
|
||||
```
|
||||
|
||||
</td><td>
|
||||
|
||||
```go
|
||||
s, err := store.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"new store: %w", err)
|
||||
}
|
||||
```
|
||||
|
||||
</td></tr><tr><td>
|
||||
|
||||
```plain
|
||||
failed to x: failed to y: failed to create new store: the error
|
||||
```
|
||||
|
||||
</td><td>
|
||||
|
||||
```plain
|
||||
x: y: new store: the error
|
||||
```
|
||||
|
||||
</td></tr>
|
||||
</tbody></table>
|
||||
|
||||
However once the error is sent to another system, it should be clear the
|
||||
message is an error (e.g. an `err` tag or "Failed" prefix in logs).
|
||||
|
||||
See also [Don't just check errors, handle them gracefully].
|
||||
|
||||
[Don't just check errors, handle them gracefully]: https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully
|
||||
Reference in New Issue
Block a user