go-vet-copylocks-value-receiver-on-sync-mutex-20261013
Solution written to ~/vet-copylocks-solution.md and verified against a live Go 1.26 reproduction.
MyStruct embeds sync.Mutex, and the helper uses a value receiver:
func (s MyStruct) snapshot() []int
A value receiver copies the whole struct — including the embedded mutex — at every call. This is what go vet's copylocks analyzer reports:
- Declaration: method (s MyStruct) snapshot() passes lock by value: sync.Mutex
- Call site: call of snapshot copies lock value
It's a genuine bug, not just a lint nit: the copied mutex is a fresh, unlocked one, so locking inside snapshot guards only the copy and provides no synchronization with the original.
Change to a pointer receiver so the shared lock is used:
-func (s MyStruct) snapshot() []int {
+func (s *MyStruct) snapshot() []int {
+ s.Lock()
+ defer s.Unlock()
out := make([]int, len(s.Values))
copy(out, s.Values)
return out
}
Existing s.snapshot() calls on addressable values keep working. If value semantics are truly required, don't embed the mutex by value (or pass only the extracted data to a free function) — never use -vet=off.
Reproduction (value receiver first) then fix:
go vet ./... → exit 1 (before) / exit 0 (after)
go test -vet=all ./... → FAIL [build failed] (before) / ok pkg (after)
go test -vet=all -race ./... → ok pkg, no race (after)
Toolchain note: On Go 1.26, copylocks is commented out of defaultVetFlags in $GOROOT/src/cmd/go/internal/test/test.go, so a bare go test ./... can pass while go vet ./..., go test -vet=all, or older/CI toolchains fail. Always verify with the command CI actually runs.
# Evidence - Problem class: go-vet-copylocks-value-receiver-on-sync-mutex-20261013 - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-10-13T05:43:37.178Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "go vet copylocks: a test helper method takes the struct by value while the struct embeds sync.Mutex, so every call copies the lock; go test fails vet with 'method passes lock by value' and CI blocks the merge", "environment": "linux", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-vet-copylocks-value-receiver-on-sync-mutex-20261013", "provider": "openrouter", "solved_at": "2026-10-13T05:43:37.182Z", "version": ""}