Go and the case of the half-missing import
One of the Go programs I keep around is gops, which I find handy for seeing if I'm using outdated binaries of Go programs that I should rebuild (for example, locally built Prometheus exporters). I have a script that updates and rebuilds all of my collection of Go programs, gops included, with the latest Go tip build (and the latest version of their code). Recently, rebuilding gops has been failing with an error:
go/pkg/mod/github.com/google/gops@[...]: reading github.com/shirou/gopsutil/go.mod at revision v3.22.11: unknown revision v3.22.11
This only happened on some systems (using one version of my rebuild script), and also didn't happen if I did a 'go install' by hand in my local cloned repository. In fact, after that manual 'go install', my scripted rebuilds started working. It took quite a while before I finally worked out what was going on.
The root cause of this error is that github.com/shirou/gopsutil retracted v3.22.11 after it accidentally slipped out. As I write this, there's no such tag in the repo and there is a commit that specifically marks v3.22.11 as retracted in go.mod (see also this gopsutil pull request about the retraction). However, before v3.22.11 was retracted Github's 'dependabot' noticed the new version and submitted a gops pull request to update its dependency, which was accepted a week later (after the retraction) in this commit.
But if the gopsutil version was retracted, how does gops build at all for anyone, and why did it start building for me? The first answer is the (default) Go module proxy, and the second answer is the module cache. The Go module proxy caches modules it's seen, so as long as one person fetched gopsutil v3.22.11 through the proxy cache before it was retracted, that version will be there for more or less all time and anyone can get a copy of it from the proxy cache. Although I had forgotten it, one set of my Go rebuild scripts deliberately turn off the module proxy so that I can spot exactly this sort of issue. When I ran 'go install' by hand, it was now talking to the proxy cache and so it got a copy of gopsutil v3.22.11. Once I had a copy, that copy was saved away in my local module cache, where it was found by the 'go' command run by my rebuild script.
(Setting GOPROXY=direct bypasses the Go module proxy and its cache, but doesn't bypass your local module cache.)
All of the individual pieces here are more or less working as designed, but the end result appears undesirable. Some of this seems to have happened because of automatic release tagging (cf), which makes that a foot-gun too, especially if things like Dependabot and Continuous Integration checks cause the default Go module proxy to fetch and cache newly tagged versions very rapidly after they become visible.