go.mod, go.sum & Minimal Version Selection
go.mod declares your module and its dependencies; go.sum locks their checksums.
Search across all documentation pages
go.mod declares your module and its dependencies; go.sum locks their checksums.
Together they give reproducible builds, and Minimal Version Selection (MVS) picks compatible versions without arbitrary "newest wins" behavior.
go.mod is the authoritative dependency contract for a module.
go.sum records h1 hashes of module archives and go.mod files the toolchain has seen.
MVS reads every requirement in the graph and selects the minimum semver version that still satisfies them all.
Understanding those three pieces explains most go get, CI drift, and "why is this version here?" questions.
Quick-reference recipe card - copy-paste ready.
go mod init example.com/app
go get github.com/stretchr/testify@v1.9.0
go mod tidy
go list -m all// go.mod (after tidy)
module example.com/app
go 1.26
require (
github.com/stretchr/testify v1.9.0
)When to reach for this:
require lines after a refactor (go mod tidy).example.com/app/
go.mod
go.sum
main.go
// main.go
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
fmt.Println(uuid.NewString())
}go get github.com/google/uuid@v1.6.0
go mod tidy
cat go.mod
grep uuid go.sumWhat this demonstrates:
require records direct dependencies you import (and sometimes tooling).go.sum gains lines like github.com/google/uuid v1.6.0 h1:... for content verification.go list -m all shows direct and transitive modules in the build list.go get).go command loads go.mod files for your module and dependencies recursively.require edge.go.sum; missing lines may be added on first trusted fetch.MVS is not a global solver that minimizes total upgrades.
It is local per module path: pick the oldest version that still meets all stated minimums.
| Directive | Purpose |
|---|---|
module | Import path prefix for this module |
go | Minimum Go toolchain/language version |
require | Module path → semver (or pseudo-version) |
exclude | Prevent selecting a specific bad version |
replace | Override source or version (local path, fork) |
retract | Mark your own bad releases (in your module's go.mod) |
Indirect dependencies appear with // indirect comments after go mod tidy when you do not import them directly.
Each module version typically has:
github.com/foo/bar v1.2.3 h1:<hash>
github.com/foo/bar v1.2.3/go.mod h1:<hash>
The /go.mod line hashes the dependency's own go.mod file.
Commit go.sum for applications and libraries so CI verifies the same content.
If module A requires C v1.2.0 and module B requires C v1.4.0, MVS selects C v1.4.0 (the minimum that satisfies the higher requirement).
If both required v1.2.0, you get v1.2.0 even when v1.9.0 exists on the proxy.
# See why a module appears
go mod why -m github.com/some/transitive
# Graph of requirements
go mod graph | head
# Verify sums without network when possible
go mod verify// toolchain directive (optional, Go 1.21+)
// go.mod
module example.com/app
go 1.26
toolchain go1.26.0go.mod by hand without go mod tidy leaves orphan requires. Prefer commands that update both go.mod and go.sum.go get -u always helps can introduce broad upgrades; review diffs and run tests.go.sum to "fix" errors removes audit trail; regenerate with tidy and intentional gets instead.// indirect does not mean unused - it means no package in your module imports it directly; you may still need it transitively.GOPRIVATE and VCS credentials - checksum DB may be skipped for private paths.go mod vendor) - Vendor source into vendor/ for offline or hermetic CI.go.work) - Local overrides without committing replace to every module.go get module@v in CI - Scripted bumps instead of floating @latest on main.An algorithm that chooses, for each module path, the lowest semver version that satisfies all require directives in the combined graph.
No for team projects.
Ignore only in throwaway experiments; production repos commit it.
A direct dependency imports another module you do not import yourself.
Tidy records it so builds stay reproducible.
Checks cached module trees and sums against go.sum.
Run in CI to detect cache corruption or tampering.
go get github.com/foo/bar@v1.1.0 then test and commit the go.mod/go.sum diff.
Only when required explicitly or when no stable version satisfies constraints.
Prefer stable tags for production.
A public log (sum.golang.org) mirroring module hashes for open modules.
Private modules set GOPRIVATE to skip it.
Your module's build merges the graph; MVS resolves to compatible versions or fails with an error during load.
Language features and toolchain behavior available when building this module.
It is not a runtime dependency like npm engines alone.
After merges that touch imports, before release tags, and in CI on every PR.
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 18, 2026