Home/Blog/Go

// Go

declared and not used in Go: why it fails and how to fix it

Every example on this page was run on 12 October 2026 in Go 1.27, and the output under each one is what it printed. How we test these posts.

declared and not used: x means your function declares a local variable x and never reads it, and Go treats that as a compile error, not a warning. Nothing runs until you fix it. Use the variable, delete it, or replace its name with the blank identifier _ when you need to receive a value you do not want.

What does declared and not used mean in Go?

Go refuses to build a function that declares a local variable it never reads. This program declares total and forgets it:

package main

import "fmt"

func main() {
	price := 20
	total := price * 3
	fmt.Println("checkout")
}

Output

main.go:7:2: declared and not used: total

The message gives the file, the line and the column of the declaration, here line 7, column 2. go build on your machine prints the same line with the path in front, as ./main.go:7:2. Nothing printed, not even checkout: the error stops the build, so the program never starts. Go 1.20 and later word it declared and not used: total. Go 1.19 and earlier printed the name first, as total declared but not used. It is the same error.

Go does this on purpose. An unused variable is often a bug: a value you computed and then forgot to return, print or check. The Go team made it an error so it cannot be ignored, and there is no flag to turn it off.

Why does Go say declared and not used when I assign the variable?

Assigning to a variable does not count as using it. Go only counts a read. This program writes status twice and reads it never:

package main

import "fmt"

func main() {
	status := "pending"
	status = "shipped"
	fmt.Println("order updated")
}

Output

main.go:6:2: declared and not used: status

The fix is the same as before: read it, as fmt.Println(status), or remove it. If you only wanted the side effect of the code on the right, keep that code and drop the variable.

Why does := inside an if or a loop cause it?

:= declares a new variable in the block it is in, even when a variable with the same name exists outside. That new variable hides the outer one, and if the block never reads it, you get declared and not used. This is the most confusing form of the error, because the name looks used:

package main

import "fmt"

func main() {
	words := []string{"go", "is", "fun"}
	count := 0
	for range words {
		count := count + 1
	}
	fmt.Println(count)
}

Output

main.go:9:3: declared and not used: count

Line 9 creates a second count that lives only inside the loop. The count on the right of := is still the outer one, so the outer count is read and never changed, and the inner one is changed and never read. The error points at the inner one, at line 9. The fix is =, which assigns to the variable that already exists:

package main

import "fmt"

func main() {
	words := []string{"go", "is", "fun"}
	count := 0
	for range words {
		count = count + 1
	}
	fmt.Println(count)
}

Output

3

The same thing happens with err. Writing result, err := parse(s) inside an if block creates a new err there, and the outer err your later check reads is never set. When the inner err is not read in the block, Go tells you; when it is, the code compiles and the bug stays, so look at every := inside a block that reuses a name from outside.

How do I ignore a value I do not need?

Give the value to the blank identifier _. It accepts any value and is never reported as unused. Functions that return two values are where you need it most, because you must receive both or neither:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	n, _ := strconv.Atoi("42")
	fmt.Println(n * 2)

	for _, word := range []string{"go", "fun"} {
		fmt.Println(word)
	}
}

Output

84
go
fun

n, _ := strconv.Atoi("42") keeps the number and drops the error. Only do that when the error truly cannot happen, as with a constant string here. With input from a user or a file, check err instead: the compiler error was telling you that you had ignored it.

for _, word := range drops the index the same way. If you need neither the index nor the value, write for range words, as in the loop above.

What does imported and not used mean?

"os" imported and not used is the same rule for packages: Go refuses to build a file that imports a package it never uses.

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("hello")
}

Output

main.go:5:2: "os" imported and not used

Delete the import. When you need a package only for what it does when it loads, such as a database driver that registers itself, import it under the blank name, as import _ "github.com/lib/pq", and Go accepts it.

Can I keep an unused variable while I debug?

You can, by reading it once with _ = x. That line counts as a use and silences the error while you work on the rest of the function:

package main

import "fmt"

func main() {
	price := 20
	total := price * 3
	_ = total
	fmt.Println("checkout")
}

Output

checkout

Treat _ = x as scaffolding. Remove it, and the variable, before you commit, or the forgotten value the compiler warned you about is still there.

Which variables does Go not check?

Go only reports local variables inside a function. A variable declared at package level, outside every function, may go unused. So may a function parameter, a named result and a constant. Those still make the code harder to read, and a linter can find them.

How do I catch it before I build?

Your editor can show declared and not used as you type. VS Code’s Go extension and other editors that use gopls, the Go language server, underline the variable the moment you stop using it. VS Code’s Go extension also organizes imports when you save, which adds the imports you use and removes the ones you do not, so imported and not used mostly disappears. From a terminal, go vet ./... compiles every package and reports both errors without running anything. staticcheck goes further and reports unused functions, constants and package-level variables, which the compiler accepts.

The Go FAQ explains why the rule exists, and the language specification on the blank identifier covers _.

Python and JavaScript accept an unused variable without a word, so the same forgotten value shows up later as a wrong result instead. In Python, a function that forgets to return its value on one path gives you TypeError: ‘NoneType’ object is not subscriptable.