The routing mismatch
You clone a repository, run your build command, and the compiler stops immediately. The error message looks like a typo, but it is actually a routing problem. Go found the code on your disk or in the module cache, but it does not trust the address you used to ask for it. The build system refuses to proceed until the import path and the declared module path match exactly.
How Go tracks module identity
Go treats the path in the module directive of a go.mod file as the single source of truth. When you import a package, Go compares the import path against that declared path. If they diverge by even a single character, the compiler rejects the program. This strict matching prevents version conflicts and ensures dependency graphs stay deterministic across every machine that builds the project.
Think of it like mailing a package to a company. The company prints its official legal name and address on its letterhead. If you address the envelope to a nickname, an old street name, or a redirected URL, the postal service rejects it. The package might physically exist at that location, but the official record does not match the label. Go enforces this rule so that go get, go build, and go mod tidy always agree on which version of the code they are talking about.
The module path is not just a label. It is the cryptographic anchor for your dependency graph. Change the anchor, and the graph breaks. Every tool in the Go ecosystem, from the standard library to third-party linters, relies on that string to identify which files belong together.
A minimal reproduction
The error appears the moment Go reads the dependency manifest and compares it to your import statement.
// go.mod
module github.com/example/mylib
go 1.22
// main.go
package main
import (
// The import path uses a hyphen, but the module directive does not
"github.com/example/my-library"
)
// Main prints a greeting using the imported library
func main() {
// This line never compiles because the import path is rejected first
println("Hello")
}
Run go build and the compiler stops with module declares its path as github.com/example/mylib but was required as github.com/example/my-library. The mismatch happens before type checking. Go reads the go.mod file, sees github.com/example/mylib, and compares it to the string you passed to import. They do not match. The build aborts.
What happens under the hood
When you run a build command, Go resolves every import path through a strict sequence. First, it checks the local module cache located in your home directory. If the module is missing, it contacts the module proxy or fetches directly from the version control system. Once the code is downloaded, Go reads the go.mod file at the root of that module. It extracts the value after the module keyword. Then it compares that value against the import path you requested.
If the comparison fails, Go throws the path mismatch error. This design protects you from accidentally pulling two different versions of the same code under different names. It also prevents supply chain attacks where a malicious actor registers a lookalike domain and tries to intercept your imports. The module proxy validates the manifest before handing it to your toolchain. If the proxy sees a mismatch between the requested path and the declared path, it returns an error before your machine even downloads the source code.
The resolution process is deterministic. Go does not guess. It does not try to fix typos. It does not follow redirects to infer a new module path. It reads the file, compares the strings, and stops if they differ. This rigidity is intentional. It forces developers to declare their intentions explicitly rather than relying on fragile heuristics.
Real-world scenarios and fixes
This error usually appears in three situations. The first is a repository rename. You move a project from github.com/olduser/pkg to github.com/newuser/pkg, but you forget to update the module directive in the go.mod file. Your local code imports the new path, but the manifest still declares the old one. The fix is straightforward. Open the go.mod file, change the module line to match the new repository URL, and run go mod tidy to refresh the dependency list.
The second situation involves vanity URLs. You want to import go.example.com/lib instead of a raw GitHub path. The vanity server must return a go.mod file that declares module go.example.com/lib. If the server returns a go.mod that says module github.com/example/lib, Go will reject the import. The convention here is simple. The module directive must always match the import path you expect developers to use. If you control the vanity server, update its manifest. If you are consuming it, import the path that the manifest actually declares.
The third situation is local development. You fork a library, change the module path to match your username, and try to build your main project against the fork. Your main project still imports the original path. Go sees the forked go.mod, notices the path mismatch, and stops. Do not hack the import paths in your main project to match the fork. Use the replace directive instead.
// go.mod
module github.com/yourname/mainapp
go 1.22
require github.com/original/lib v1.2.0
// Override the remote module with a local directory during development
replace github.com/original/lib => ../path/to/your/fork
The replace directive tells Go to fetch the code from your local directory but still validate it against the original module path. This keeps your dependency graph clean and makes it trivial to remove the override when you are ready to publish. Run go mod tidy after adding a replace directive to ensure the go.sum file stays consistent.
Pitfalls and compiler behavior
Developers often try to force the build by manually editing go.sum or deleting the module cache. Both approaches waste time. The module cache is just a copy of what the proxy gave you. If the manifest declares the wrong path, deleting the cache only forces Go to download the same mismatched file again. The compiler will reject the program with module declares its path as X but was required as Y every single time until the paths align.
Another common mistake is assuming go mod tidy will fix a broken module directive. The tidy command only adds or removes require lines based on your imports. It does not rewrite the module line. If your go.mod says module github.com/wrong/path, go mod tidy will not change it to github.com/correct/path. You must edit the module directive manually or run go mod edit -module github.com/correct/path. The go mod edit command is the standard way to manipulate the manifest without risking accidental formatting changes.
Watch out for case sensitivity. Go module paths are case sensitive. github.com/Example/Lib and github.com/example/lib are treated as completely different modules. The compiler complains with module declares its path as github.com/example/lib but was required as github.com/Example/Lib if you mix them up. Most Git hosting platforms normalize repository names to lowercase, but the go.mod file preserves whatever you typed. Always match the casing exactly.
The go.mod file is the contract. Trust the file, not your memory of where the code lives.
When to use which approach
Use go mod tidy when your imports are correct but your go.mod or go.sum is out of sync with the actual code.
Use a replace directive when you are developing a local fork and need to override the remote path temporarily without breaking the dependency graph.
Use a vanity URL when you want a stable import path that survives repository renames, provided the vanity server returns a go.mod that declares that exact vanity path.
Use a direct version control path when you are prototyping and do not need a custom domain.
Keep the module directive in sync with your actual repository URL. The build system trusts the file, not your intentions.