The missing checksum problem
You clone a repository, run go run main.go, and the terminal immediately prints missing go.sum entry for module. You did not change any imports. The code compiled fine yesterday. The build stops dead. This is not a syntax error. It is not a missing package. It is Go's module system refusing to run code it cannot verify.
The Go toolchain treats every external dependency as untrusted until proven otherwise. When you add a package to your project, Go records two things. The first is the module path and version in go.mod. The second is a cryptographic hash of the exact bytes in go.sum. If those two files fall out of sync, the compiler aborts. It would rather fail fast than execute tampered code.
What go.sum actually does
Think of go.mod as a shopping list. It tells Go what you want to buy and which version you expect. go.sum is the tamper-evident seal on the delivery box. It proves the package you received matches exactly what the author published.
When you import a third-party library, Go contacts the module proxy. The default proxy is proxy.golang.org, which caches every public Go module. The proxy hands back a .zip file containing the source code. Go extracts it, computes a SHA256 hash of the contents, and compares that hash against the entry in go.sum. If the hash matches, the build continues. If the hash is missing or different, Go throws missing go.sum entry for module and stops.
This design blocks a specific class of supply chain attacks. If a network attacker intercepts your connection, or if the proxy gets compromised, they could swap in a malicious version of a library. Without go.sum, your project would happily compile the poisoned code. With it, the hash check fails and the build refuses to proceed. The toolchain prioritizes security over convenience.
Convention aside: go.sum is auto-generated. You never edit it by hand. The community treats it as a first-class artifact. Commit it to version control alongside go.mod. If your .gitignore filters it out, your CI pipeline will eventually break.
Trust the checksums. Commit the file.
Minimal example
Here is the simplest way to reproduce the error. Create a new module, add a dependency manually to go.mod, and try to build without regenerating the checksums.
package main
import (
// Import a standard library to keep the file runnable
"fmt"
// Import an external package that is not yet in go.sum
"github.com/example/pkg"
)
func main() {
// Print a greeting to verify the build succeeds
fmt.Println("Hello, module system")
// Call the external package to trigger the dependency check
pkg.Run()
}
# Manually add the dependency to go.mod without running tidy
go get github.com/example/pkg@v1.0.0
# Remove the checksum file to simulate a missing entry
rm go.sum
# Attempt to build the program
go build ./...
# output:
# go: github.com/example/pkg@v1.0.0: missing go.sum entry for module; to add it:
# go mod download github.com/example/pkg
The compiler rejects the program with missing go.sum entry for module because it cannot verify the dependency. The fix is to let Go recalculate the graph and regenerate the checksums. go mod tidy scans every file in your module for import statements. It compares those imports against go.mod. It removes unused dependencies, adds missing ones, and fetches the exact versions from the proxy. Finally, it writes the correct hashes to go.sum. Running it is the safest approach because it aligns your module files with your actual source code. If you are working in a workspace with a go.work file, run the command from the workspace root. The toolchain will synchronize all referenced modules at once.
Never hand-edit go.sum. Let the compiler own the hashes.
Realistic workflow
Picture a team building an HTTP service. One developer adds a new logging library. They update go.mod manually to pin a specific version. They forget to run go mod tidy. They push the code. The CI runner checks out the branch, runs go build, and immediately hits missing go.sum entry for module. The pipeline fails. The developer pulls the branch, runs go mod tidy, commits the updated go.sum, and pushes again. The build passes.
Here is how that handler looks in production code. The import graph grows quickly, and every new line triggers a checksum verification.
package main
import (
// Standard library for HTTP routing
"net/http"
// External logger that requires a valid go.sum entry
"go.uber.org/zap"
// Database driver that pulls in indirect dependencies
"github.com/lib/pq"
)
// NewServer creates and configures the HTTP router
func NewServer() *http.Server {
// Initialize the structured logger
logger, _ := zap.NewProduction()
defer logger.Sync()
// Register the database driver for later use
_ = pq.Driver()
// Return a configured server instance
return &http.Server{
Addr: ":8080",
Handler: http.DefaultServeMux,
}
}
This workflow prevents accidental version drift. If two developers add different versions of the same library, go mod tidy resolves the conflict by picking the highest required version. It updates go.mod and regenerates go.sum. The next developer who pulls the branch gets the exact same graph. Reproducible builds are not a luxury in Go. They are the default.
If you are behind a corporate firewall, the default proxy might be unreachable. You can configure a custom proxy by setting the GOPROXY environment variable. The toolchain will fetch modules from your internal mirror instead. The checksum verification still runs. go.sum still gets populated. The security model does not change.
Keep your dependency graph tight. Drift breaks builds.
Pitfalls and cache behavior
The compiler will not guess what you meant. If go.sum is missing an entry, it stops. You will see missing go.sum entry for module in the build output. You might also see verifying module checksums followed by a mismatch error if the cache is corrupted. Both point to the same root cause: the toolchain cannot prove the dependency is authentic.
Do not try to patch go.sum manually. The file contains hashes for every module version, plus indirect dependencies. Adding a line by hand often introduces formatting errors or mismatched hashes. The next go mod tidy will overwrite your changes anyway. Let the toolchain manage the file.
If you are using a monorepo with multiple go.mod files, each module maintains its own go.sum. Running go mod tidy inside a subdirectory only updates that module. Use go work sync to align all modules in the workspace. The workspace file acts as a coordinator, but the checksums still live in the individual go.sum files.
Sometimes go mod tidy fails to resolve the entry. The network might be flaky, or your local module cache might contain a corrupted download. Go stores fetched modules in $GOPATH/pkg/mod. Each module gets a directory named after its path and version, plus a .info and .zip file. If the cache gets interrupted mid-download, the next build will try to use a broken archive and fail with a checksum mismatch. You can force the toolchain to refetch everything by clearing the cache.
# Wipe the local module cache to discard corrupted downloads
go clean -modcache
# Rebuild the dependency graph and regenerate checksums
go mod tidy
This deletes the entire module cache and forces a fresh download. It takes longer, but it guarantees you are working with clean files. Use it when tidy repeatedly fails with hash mismatches or when you suspect a corrupted proxy response.
Convention aside: go mod tidy is the standard pre-commit step. Most teams add it to their linting hooks or run it in CI to catch drift. The command is idempotent. Running it twice produces the same result as running it once. Error handling in Go is explicit. The module system follows the same philosophy. It does not hide dependency resolution behind magic. It prints exactly what failed and why. Read the error. Fix the graph. Move on.
The module system is strict by design. Trust the checksums. Commit the file.
Decision matrix
Use go mod tidy when you add, remove, or change imports and need to synchronize go.mod and go.sum with your actual source code. Use go mod download when you only need to fetch missing modules into the local cache without updating the dependency graph. Use go clean -modcache when checksum mismatches persist and you suspect a corrupted local download or proxy response. Use go work sync when you are in a workspace and need to align multiple module graphs at once. Reach for GOPROXY configuration when your network blocks the default proxy and you need to route module requests through an internal mirror.