go vet & Common Analyzer Diagnostics
go vet inspects Go source for constructs that compile but are probably wrong.
Search across all documentation pages
go vet inspects Go source for constructs that compile but are probably wrong.
Every team should run go vet ./... locally and in CI, understand the common diagnostics, and know when deeper analyzers add value.
Quick-reference recipe card - copy-paste ready.
# All packages in module
go vet ./...
# Specific packages
go vet ./internal/...
# List available analyzers (Go 1.26+)
go tool vet help
# Run vet via test harness (some teams prefer)
go test -vet=off ./... # disable vet during test when running separatelyWhen to reach for this:
Buggy handler:
package api
import (
"encoding/json"
"fmt"
"net/http"
)
type User struct {
Name string `json:name` // invalid tag syntax
}
func Handler(w http.ResponseWriter, r *http.Request) {
u := User{Name: "ada"}
_ = json.NewEncoder(w).Encode(u)
fmt.Printf("user %d", u.Name) // format verb mismatch
}Run:
go vet ./...Typical output:
api/handler.go:12:2: fmt.Printf format %d has arg u.Name of wrong type string
api/handler.go:8:5: struct field tag `json:name` not compatible with reflect.StructTag.Get: bad syntax for struct tag pairFixed code:
type User struct {
Name string `json:"name"`
}
func Handler(w http.ResponseWriter, r *http.Request) {
u := User{Name: "ada"}
_ = json.NewEncoder(w).Encode(u)
fmt.Printf("user %s", u.Name)
}What this demonstrates:
go test.go/analysis).GOOS, and GOARCH affect which files vet examines.| Analyzer | What it flags | Example fix |
|---|---|---|
printf | Format verbs vs argument types | Match %s, %d, %v to args |
structtag | Malformed or conflicting struct tags | Fix json:"name" quoting |
unsafeptr | Invalid unsafe.Pointer conversions | Redesign unsafe code or add tests |
copylocks | Copying structs containing locks | Use pointer receivers or avoid copy |
loopclosure | Goroutine captures loop vars (pre-1.22 footgun) | Pass param or use Go 1.22+ semantics |
lostcancel | context.WithCancel without cancel() call | defer cancel() |
httpresponse | http.Response body not closed on error paths | defer resp.Body.Close() with err check |
testinggoroutine | t.Fatal from non-test goroutine | Signal failure via channel or t.Error + sync |
golangci-lint's govet linter wraps vet analyzers and may enable additional checks.
Running go vet alone is simpler in scripts; golangci-lint centralizes severity and excludes.
# .golangci.yml excerpt
linters:
enable:
- govet
linters-settings:
govet:
enable-all: true// copylocks example - vet flags copying sync.Mutex
type Bad struct {
mu sync.Mutex
}
func use(b Bad) { // vet: passes lock by value
b.mu.Lock()
}Prefer pointer receivers or do not embed locks in copied structs.
| Need | Reach for |
|---|---|
| Dead code, API deprecation | staticcheck (SA*) |
| Nil dereference paths | nilaway, staticcheck |
| Security smells | gosec |
| Custom org APIs | go/analysis plugin |
go vet on upgrade branch early.main packages - Bugs hide in internal/. Fix: Always go vet ./....copylocks in hot paths - Passing locked structs copies mutex state. Fix: Use pointers or redesign API.%v deliberately with comment.GOFLAGS.//nolint:govet broadly - Hides future real issues. Fix: Narrow suppressions to one line with justification ticket.| Alternative | Use When | Don't Use When |
|---|---|---|
| golangci-lint only | Need one config for 30+ linters | You want zero YAML and fast scripts |
| staticcheck alone | Deeper correctness, SA codes | You need official toolchain checks in minimal CI |
| Compiler errors only | Never for production Go | - |
| Runtime tests only | Complements vet | Replacing static analysis for tag/printf bugs |
No.
It targets high-confidence suspicious patterns.
Tests, race detector, and staticcheck fill other gaps.
Order is flexible.
Many pipelines run go vet and go test in parallel jobs to save wall time.
Different Go versions, build tags, or files only present in CI checkout.
Align go version and environment variables.
Before Go 1.22, loop variables were reused, so goroutines saw the final value.
Go 1.22+ creates per-iteration variables; still worth understanding for libraries supporting older Go.
structtag fails at compile/vet time for malformed tags.
Marshal errors often appear only at runtime for unsupported field types.
In golangci-lint, tune govet settings.
Plain go vet uses the toolchain default set for your Go version.
Yes when you vet packages that include tests.
Some analyzers have test-specific rules (testinggoroutine).
Yes if it is compiled.
If generators emit known vet noise, exclude paths in golangci-lint rather than skipping vet entirely.
Separate tools.
go fix applies rewrites; go vet reports problems without auto-fix (except via editor quick fixes for some analyzers).
Yes (unreachable analyzer).
Often indicates missing return or dead branches after refactor.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026