Quick answer
A package is in your import block but never referenced in the file. This is a hard compiler error in Go, not a linter warning like in most languages. Either delete the unused import, actually use it (you likely deleted the only line that did), or if you need it purely for its side effects (like a driver registration), prefix it with a blank identifier: import _ "pkg".
The exact error string
$ go build .
./main.go:4:2: imported and not used: "fmt"
./handler.go:6:2: imported and not used: "encoding/json" as jsonlib
Notice this is not a warning printed alongside a successful build — it is a hard failure with a file:line:column location, and go build exits non-zero, producing no binary at all. Go is unusual among mainstream languages in refusing to compile a file with a dead import; most compilers either ignore unused imports entirely or leave the concern to an external linter that a team may or may not have wired into CI.
Cause 1: leftover import after removing the code that used it
import (
"fmt"
"os"
)
func main() {
// fmt.Println("debug:", os.Args) // ← commented out during debugging
os.Exit(0)
}
// ❌ "fmt" is no longer referenced anywhere — imported and not used: "fmt"
// ✅ either restore a real use of fmt, or remove the import line entirely:
import "os"
This is overwhelmingly the most common trigger: a debugging fmt.Println gets commented out, a function that used a package gets deleted during a refactor, or a code path gets simplified away — and the import statement, sitting separately at the top of the file, is easy to forget about entirely. Go's compiler doesn't care why the import is now dead; it applies the same hard rule regardless of how the file arrived at this state.
Cause 2: a typo'd or renamed identifier means the import looks unused
import "strings"
func clean(s string) string {
return strngs.TrimSpace(s) // ❌ typo — this isn't "strings" at all
}
// the compiler sees two separate problems here:
// undefined: strngs
// imported and not used: "strings"
// ✅ fix the typo so the reference actually resolves to the import
func clean(s string) string {
return strings.TrimSpace(s)
}
A misspelled reference to a package doesn't just produce an undefined identifier error — since the import is never actually referenced under its correct name, the compiler reports both errors for the same underlying typo. Seeing this pair together (an undefined identifier that looks suspiciously like a package name, plus an unused-import error for that exact package) is a strong signal the real bug is a single typo, not two separate problems.
Cause 3: importing a package purely for its side effects, without the blank identifier
// ❌ imported to register a database driver via its init(), but the
// package name itself is never referenced in this file:
import "github.com/lib/pq"
func main() {
db, _ := sql.Open("postgres", dsn) // driver is used via sql.Open's string
}
// → imported and not used: "github.com/lib/pq"
// ✅ the blank identifier signals "intentionally side-effect-only":
import _ "github.com/lib/pq"
Some packages are designed to be imported purely so their package-level init() function runs — registering a SQL driver with database/sql, an image codec with image, or a profiling endpoint with net/http/pprof are the classic examples. Since the package's exported identifiers are never directly referenced in these cases, Go needs an explicit signal that the omission is intentional rather than accidental dead code; the blank identifier _ as the import name is exactly that signal, and it's the only way to satisfy the compiler for a legitimately side-effect-only import.
How goimports prevents most of this
goimports is a drop-in superset of gofmt that, in addition to formatting, automatically removes import lines that have become unreferenced and adds missing ones it can confidently infer from unresolved identifiers matching well-known package paths. Most Go editor integrations (VS Code's Go extension, GoLand, vim-go) run it on every save by default, which is why many Go developers rarely see this error directly — the tooling silently deletes the dead import before the file is ever built. Running goimports -w . manually across a project is a fast way to clean up a batch of these after a large refactor.
Common variants at a glance
| Situation | Root cause | Fix |
|---|---|---|
| appeared after commenting out debug code | the only reference to the package was removed | delete the import, or restore a real use |
| paired with an "undefined" error for a similar name | a typo means the reference never resolves to the import | fix the typo so it matches the import |
| driver/codec registration pattern | package is needed only for its init() side effect | prefix with the blank identifier: import _ "pkg" |
| happens repeatedly across a project | editor isn't running goimports on save | enable goimports in your editor, or run it manually |
Why this happens: Go treats dead code as a build-blocking defect, not a style nit
Most languages leave the "unused import" concern to an optional linter, precisely because reasonable teams disagree about how strict to be about dead code — some want it flagged, some want it auto-removed, some don't care. Go's designers deliberately removed that choice from the equation: an unused import is folded into the same hard error class as a syntax error, alongside an undefined identifier. The stated rationale from the language's authors is that unused imports accumulate silently in large codebases if left to convention, each one adding a small, real cost (slower compilation, unclear dependency graphs, confusion about what a file actually needs) that compounds across thousands of files. Making it a compiler error rather than a lint rule guarantees every Go codebase — not just ones with a particular linter configured in CI — stays free of this specific kind of drift.
Debugging checklist
- ✓ Check whether you recently commented out or deleted the only code that used this import
- ✓ Look for a paired "undefined" error on a similarly-spelled identifier — it's likely one typo, not two bugs
- ✓ If the package is needed only for its
init()side effect, prefix it with_ - ✓ Run
goimports -w .to auto-clean a whole project after a big refactor - ✓ Enable
goimports-on-save in your editor to stop seeing this on the command line at all
Frequently Asked Questions
What does "imported and not used" mean in Go?
You added a package to a file's import block but never referenced it anywhere in that file's code. Unlike most languages, where an unused import is at most a linter warning, Go's own compiler treats it as a hard compile error and refuses to build the package at all.
Why is this a compile error instead of just a warning?
It's a deliberate language design decision by Go's creators, aimed at keeping codebases clean by construction rather than by convention. An unused import usually signals leftover code from a refactor or an incomplete edit, and Go's designers decided that tolerating it — even with a warning — leads to codebases that accumulate dead imports over time. Making it a hard error forces immediate cleanup.
How do I import a package purely for its side effects, without using it directly?
Prefix the import with a blank identifier: import _ "database/sql/driver-name". This tells the compiler you're intentionally importing the package only to run its package-level init() function (commonly used to register a database driver, an image format decoder, or an HTTP handler) and suppresses the unused-import error entirely for that import.
Why did this appear right after I deleted or commented out some code?
Removing the only line that referenced a package (commenting it out while debugging, or deleting a function during a refactor) leaves the import behind with nothing left to use it. This is the single most common trigger — the import was correct when written, and became stale the moment its only caller was removed.
Does goimports fix this automatically?
Yes, for the removal side: goimports (a superset of gofmt that most Go editors run on save) automatically deletes import lines that aren't referenced anywhere in the file, and adds missing ones for identifiers it recognizes. Running it, or configuring your editor to run it on save, eliminates most instances of this error before you ever see it on the command line.
Does an unused import in a _test.go file cause the same error?
Yes — test files are compiled by the same Go compiler and follow the identical unused-import rule as any other .go file. There's no relaxed mode for tests; an accidentally-left-behind testing import after removing the only test that used it fails the build the same way production code would.
More Go, Rust & Java errors
Browse the full reference for Go, Rust, Java, and other backend language errors — exact message, cause, and fix.