Skip to content

cmd/go: test -coverprofile writes cached coverage blocks again when the cached test result is invalid #81824

Open

Description

@eltonvs

Go version

go version go1.27.1 darwin/arm64

Output of go env in your module/workspace:

AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS='-I/opt/homebrew/include'
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-L/opt/homebrew/lib'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN='/Users/elton/go/bin'
GOCACHE='/Users/elton/lab/repro/cache'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/elton/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/h1/kf1j70fs3d9g7g2lm_t8c45w0000gn/T/go-build3078940608=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/elton/lab/repro/go.mod'
GOMODCACHE='/Users/elton/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPACKAGESDRIVER=''
GOPATH='/Users/elton/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/elton/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.27.1.darwin-arm64'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/elton/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/elton/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.27.1.darwin-arm64/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.27.1'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

go test -coverprofile can reuse a cached test result and produce an incorrect coverage profile.

cat > go.mod <<'EOF'
module m

go 1.27.1
EOF

cat > main.go <<'EOF'
package main

func Abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}
EOF

cat > main_test.go <<'EOF'
package main

import "testing"

func TestAbs(t *testing.T) {
	Abs(-1)
}
EOF

go test -coverprofile=c1.out .
go test -coverprofile=c2.out .   # (cached)
go clean -testcache
go test -coverprofile=c3.out .
wc -l c1.out c2.out c3.out
sort c3.out | uniq -c

What did you see happen?

c3.out triples the output of c1.out / c2.out

$ wc -l c1.out c2.out c3.out
       4 c1.out
       4 c2.out
      10 c3.out
      18 total
$ sort c3.out | uniq -c
   3 m/main.go:4.2,4.11 1 1
   3 m/main.go:5.3,6.1 1 1
   3 m/main.go:7.2,7.10 1 0
   1 mode: set

go tool cover -func=c3.out accepts the file with the duplicated lines and reports the normal percentage, silencing the corruption. However, when using go test with -covermode=count or atomic, the hit counts are tripled.

In a multi-package run, only the affected package is duplicated.

What did you expect to see?

The c3.out output should be identical to c1.out, containing each block once. The cached profile should be merged only when tryCacheWithID returns true.

Impact: the profile is corrupted silently. Tools that reject duplicate blocks fail (for example a CI step that merges shard profiles).

Activity

  1. added a commit that references this issue on Sep 28, 2026
    02cd3db
  2. gopherbot commented on Sep 28, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/840485 mentions this issue: cmd/go: merge cached cover profile only when the cached result is used

  3. prattmic commented on Sep 29, 2026

    @prattmic
    Member

    cc @golang/command-line

  4. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions