This is a collection of subtle bugs I’ve encountered in Go that took time to understand. Each entry documents the issue, a reproducible example, and the root cause
1. HTTP call to 3rd party API responds with error from our local database
Diagnosis: I see a weird error in the logs, it looks like this:
error happened GET https://example.com, <service database error>
where you can see endpoint URL and denoted with angle brackets an error from our own database. What the heck? How come we get an error from our own database when we call an endpoint?!
Below you will find a completely reproducible example:
| |
The output will be:
client.Do error: Get "http://127.0.0.1:41339": mongodb_error_poison
How come? What is going on? Any guesses?
It turns out the following is happening:
errgroup.Wait() will cancel the context as soon as any of goroutines return an error.
Internally, errgroup will use a special context.WithCancelCause() context which returns the context itself
and a cancel function that accepts an error as a cause.
Now, as soon as the context is cancelled, http.DefaultClient.Do(req) with a request that has our context immediately returns an error.
But the method Do() has this comment:
// Any returned error will be of type [*url.Error].
and the url.Error struct looks like this:
// Error reports an error and the operation and URL that caused it.
type Error struct {
Op string
URL string
Err error
}
Long story short. The “cause” from the context, which is an error, will be assigned to the Err field in url.Error.
This is how the database error bubbled up from the context and was returned from the HTTP call.