What version of Go are you using (go version)?
$ go version
go version go1.18.4 darwin/arm64
$ git version
git version 2.37.1
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
go env Output
$ go env
% go env
GO111MODULE="auto"
GOARCH="arm64"
GOBIN=""
GOCACHE="REDACTED"
GOENV="REDACTED"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="REDACTED"
GONOPROXY="REDACTED"
GONOSUMDB="REDACTED"
GOOS="darwin"
GOPATH="REDACTED"
GOPRIVATE="REDACTED"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.18.4"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/vy/ryp4d95n75q5j6kf58hc551m0000gn/T/go-build559884269=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
- Create/identify 3 go modules/repos:
A depended on by B depended on by C (so C indirectly depends on A)
- make B's
go.mod reference to A invalid
(for us, A's git history was rewritten; but you could probably manually edit B's go.mod with a invalid pseudo-version to get the same effect)
- run
go get -u ./... in either B or C (or any module that depends on either of them)
- wait for the
go process to hang with high CPU, then terminate it
- run
go list -m -u -json all | jq --raw-output 'select((.Indirect or .Main)|not) | .Path' | xargs -t -I % go get -u -t %@latest, to find any "error:" or "panic:" output
- Revert any changes to
go.* files resulting from step 5.
What did you expect to see?
Either some output indicating the dependency reference problem(s), or else normal operation of the go get ... command in step 3.
What did you see instead?
Step 3 starts running normally (there may or may not be some output, downloads, etc., with typical levels of CPU by the process), but at some point all output ceases and CPU spikes to 350%-450% and stays basically flat at that level until the go process is terminated.
Step 5 did not hang, and output contained several sections with errors like these:
go: downloading github.com/private-org/module-two v0.0.0-20220713230938-ae346380ff64
go: github.com/private-org/module-one@v0.0.0-20220203083605-df6cd891c11a: invalid version: unknown revision df6cd891c11a
go get -u -t github.com/private-org/module-three@latest
go: github.com/private-org/module-one@v0.0.0-20220203083605-df6cd891c11a: invalid version: unknown revision df6cd891c11a
Workaround
- Add a
replace directive for each of the "invalid version"s identified in step 5 to the go.mod files of each dependent repo (including indirectly-dependent ones).
- Repeat steps 3 through 7 until step 3 finishes normally (i.e. without hanging)
Notes:
- The hanging behavior happened consistently (100% repeatable; always hanged) on only some of the M1 macs tested. Others with identical hardware and similar go, git, etc. versions, even when running exactly the same commands on the same repos. Not sure why the issue only affected some envs and not others.
- The root cause for us was that 3 repos had at least 1 pseudo-version reference (by other repos) to a commit which no longer existed in the referenced repo. Once the workaround above was completed for each missing revision, the unexpected
go get behavior ceased.
- I only identified a current
go.mod reference to 1 of the 4 "missing revision"s which were in the error output. The other 3 seemed to be referenced exclusively from historical versions of go.mod and go.sum files (e.g. the git history of no-longer-referenced versions of certain modules), but not by any versions which were still directly referenced. IDK how un-referenced versions' go.* contents could still be affecting go get behavior, but it seemed to be the case. Very weird. If true, more details would be appreciated, since that might indicate that the replace directives need to remain in place forever (which would be unfortunate).
- All repos involved are private repos, but IDK whether that matters or not. I suspect not. (All of the usual "private repo"-related config issues are not an issue for us, since all commands work fine on some machines, and work fine for repos with un-corrupted
go.* files even on affected envs.)
- The hanging happened with latest git and go versions (installed yesterday morning), and also with older versions of go (v1.18) and git. (IDK whether the git version matters or not, since upgrading seemed to have no effect.)
- All of the repos involved had
go 1.17 or go 1.18 directives in their go.mod files. We weren't attempting to preserve pre-v1.17 compatibility.
- Adding git tags with names equal to the missing pseudoversions (e.g.
v0.0.0-20220203083605-df6cd891c11a) did not work as a workaround.
- It would be nice to have a tag-based way to retroactively "fix"
go.mod refs which get broken as a result of commits which get irrevocably deleted. That would be much preferred vs the replace directives workaround.
- Adding
retract directives to the repos which were the source of the "missing" versions did not resolve the problem. (Would've been much nicer if it had, so that the same replace directives didn't need to be replicated in dozens of dependent repos.)
- I know that
retract isn't intended to prevent a dependent repo from referencing a retracted version once it already is, but since I was actually trying to upgrade all the deps, it would've been nice if the go get -u ./... command simply updated the "bad" refs with good ones (and didn't hang), which would have allowed me to iteratively upgrade all dependencies to stop using the bad revisions (without the need to manually identify and replace the "missing" revisions).
What version of Go are you using (
go version)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env)?go envOutputWhat did you do?
Adepended on byBdepended on byC(so C indirectly depends on A)go.modreference to A invalid(for us, A's git history was rewritten; but you could probably manually edit B's go.mod with a invalid pseudo-version to get the same effect)
go get -u ./...in either B or C (or any module that depends on either of them)goprocess to hang with high CPU, then terminate itgo list -m -u -json all | jq --raw-output 'select((.Indirect or .Main)|not) | .Path' | xargs -t -I % go get -u -t %@latest, to find any "error:" or "panic:" outputgo.*files resulting from step 5.What did you expect to see?
Either some output indicating the dependency reference problem(s), or else normal operation of the
go get ...command in step 3.What did you see instead?
Step 3 starts running normally (there may or may not be some output, downloads, etc., with typical levels of CPU by the process), but at some point all output ceases and CPU spikes to 350%-450% and stays basically flat at that level until the
goprocess is terminated.Step 5 did not hang, and output contained several sections with errors like these:
Workaround
replacedirective for each of the "invalid version"s identified in step 5 to thego.modfiles of each dependent repo (including indirectly-dependent ones).Notes:
go getbehavior ceased.go.modreference to 1 of the 4 "missing revision"s which were in the error output. The other 3 seemed to be referenced exclusively from historical versions ofgo.modandgo.sumfiles (e.g. the git history of no-longer-referenced versions of certain modules), but not by any versions which were still directly referenced. IDK how un-referenced versions'go.*contents could still be affectinggo getbehavior, but it seemed to be the case. Very weird. If true, more details would be appreciated, since that might indicate that thereplacedirectives need to remain in place forever (which would be unfortunate).go.*files even on affected envs.)go 1.17orgo 1.18directives in theirgo.modfiles. We weren't attempting to preserve pre-v1.17 compatibility.v0.0.0-20220203083605-df6cd891c11a) did not work as a workaround.go.modrefs which get broken as a result of commits which get irrevocably deleted. That would be much preferred vs thereplacedirectives workaround.retractdirectives to the repos which were the source of the "missing" versions did not resolve the problem. (Would've been much nicer if it had, so that the samereplacedirectives didn't need to be replicated in dozens of dependent repos.)retractisn't intended to prevent a dependent repo from referencing a retracted version once it already is, but since I was actually trying to upgrade all the deps, it would've been nice if thego get -u ./...command simply updated the "bad" refs with good ones (and didn't hang), which would have allowed me to iteratively upgrade all dependencies to stop using the bad revisions (without the need to manually identify andreplacethe "missing" revisions).