Go undefined: X — variable or symbol not found
undefined: X (variable or symbol not found)
Verified against Go spec: Declarations and scope, Go spec: Short variable declarations, Effective Go: Names · Updated June 2026
> quick_fix
The identifier you're using doesn't exist in the current scope. Either you forgot to declare it, misspelled it, or it's in a different package that isn't imported. Check the spelling, confirm the variable is declared in the same or enclosing scope, and verify any required package is imported.
// Error: undefined: count
func sum(nums []int) int {
for _, n := range nums {
count += n // compile error: undefined: count
}
return count
}
// Fix — declare count before the loop
func sum(nums []int) int {
count := 0 // declare and initialize
for _, n := range nums {
count += n
}
return count
}What causes this error
Go requires every identifier to be declared before use. 'undefined: X' fires when: (1) you use a variable that was never declared, (2) the variable was declared in a different scope (inside an if/for block) and used outside it, (3) the package containing the symbol isn't imported, (4) the symbol is unexported from another package (lowercase name), or (5) a `:=` inside a block creates a new variable shadowing an outer one.
How to fix it
- 01
step 1
Declare the variable before using it
Go does not allow use before declaration. Declare variables with `var x T` or the short form `x := value` before the first use.
// BAD — using before declaring result += compute() // undefined: result // GOOD — declare first var result int result += compute() - 02
step 2
Watch for scope: variables declared inside blocks
A variable declared inside an `if`, `for`, or `switch` block is only visible inside that block. Declare it before the block if you need it after.
// BAD — err is declared inside if block, used outside if condition { err := doSomething() // err scoped to this block } fmt.Println(err) // undefined: err // GOOD — declare err before the if var err error if condition { err = doSomething() // assign, not declare } fmt.Println(err) - 03
step 3
Check package imports for symbols from other packages
If you use `fmt.Println` without importing `"fmt"`, you get `undefined: fmt`. Import every package you use. Use `goimports` or the IDE to manage imports automatically.
// BAD — using fmt without import package main func main() { fmt.Println("hello") // undefined: fmt } // GOOD — import the package package main import "fmt" func main() { fmt.Println("hello") } - 04
step 4
Check exported vs unexported names from other packages
In Go, names starting with a lowercase letter are unexported — they're only visible within the same package. If you try to use `pkg.myFunc` from outside `pkg`, you get 'undefined: pkg.myFunc'. The fix is either to export the name (capitalize it) or access it from within the same package.
// In package mylib: func helper() {} // unexported — only visible inside mylib func Helper() {} // exported — visible everywhere // From another package: mylib.helper() // compile error: cannot refer to unexported name mylib.Helper() // ✅ works - 05
step 5
Watch for := shadowing in if-err patterns
Using `:=` in an if-init statement creates a new variable scoped to the if block. If you want to assign to an existing outer variable, use `=` for assignment, or mix `=` and `:=` carefully.
var user *User var err error // BAD — := creates new user and err scoped to the if block if user, err := findUser(id); err != nil { log.Fatal(err) } fmt.Println(user) // undefined: user (the outer user is still nil) // GOOD — = assigns to outer variables user, err = findUser(id) if err != nil { log.Fatal(err) } fmt.Println(user)
How to verify the fix
- The file compiles without errors (`go build ./...`).
- All variables are declared before first use.
- All imported packages are used (Go refuses unused imports).
Why undefined: X happens at the runtime level
Go's compiler performs a scope resolution pass that maps every identifier to its declaration. An identifier that cannot be resolved to any declaration in any enclosing scope (file, block, package, universe) causes a compile error 'undefined: X'. Scope in Go is lexical: inner blocks can see outer block declarations, but not vice versa. `:=` in an inner block creates a new variable there, not in any outer block. The compiler's undefined error is generated by the types.Checker in `go/types` during the type-checking phase, which runs after parsing.
Common debug mistakes for undefined: X
- Declaring a variable inside an `if` or `for` block with `:=` and attempting to use it after the block closes.
- Using `err` from an inner `if err := ...; err != nil {}` init statement outside the if — the init `err` is block-scoped.
- Forgetting to import a package after moving a call from one file to another.
- Using a symbol from a package without its package prefix — `Println` instead of `fmt.Println`.
- Relying on C-style implicit fallthrough variable declaration — Go has no implicit variable hoisting.
When undefined: X signals a deeper problem
Scope confusion with `:=` is the most persistent Go beginner pitfall. The multi-assignment form `a, err := fn()` in Go only declares variables that don't already exist in the current scope — if `err` is already declared in the outer scope and `a` is new, `:=` declares `a` and assigns to the existing `err`. But when the whole statement is inside a new block (if, for), all variables become block-scoped, shadowing outer ones. This is subtle enough that experienced Go developers hit it occasionally. `go vet` and `staticcheck` have rules that flag shadowing (`SA1006`, `-shadow` flag on `go vet`). Adding these to CI prevents the most confusing instances from reaching code review.
Editor's take
The undefined compile error is the most benign error in Go — it never reaches production because Go won't compile code with undefined identifiers. The learning value is understanding Go's scoping rules, specifically the `:=` shadow trap.
The pattern that confuses developers coming from Python or JavaScript most: in those languages, variables declared inside an `if` block are visible outside it. In Go, they're not. A Python developer writing their first Go program will frequently declare a variable inside an if-else and try to use it after, triggering 'undefined'. The mental model shift is: in Go, every `{` starts a new scope, and declarations inside don't leak out.
The version of this that reaches production in subtle form: the `:=` shadow in goroutines. A developer writes `go func() { result, err := compute(); if err != nil { log.Fatal(err) }; results = append(results, result) }()` where `results` is the outer variable. This compiles and runs, but if the goroutine runs after `results` has been reassigned in the outer scope, the append targets the old slice. Not an 'undefined' error, but the same scoping confusion causing a data race instead. The race detector (`go test -race`) catches this at runtime. The undefined compile error is Go's compiler doing you a favor — catching the declaration problem at compile time so it never reaches the subtler runtime form.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
Why does Go refuse to compile if a variable is declared but not used?
Unused variables are a common source of bugs — a developer declares a variable, modifies it, but reads the old value by mistake. Go's compiler making this a compile error forces developers to remove dead variable declarations, reducing confusion. Unused imports are similarly banned. If you need to declare a variable for its side effects only, assign it to the blank identifier: `_ = err`.
What is the difference between := and = in Go?
`:=` is the short variable declaration — it declares AND assigns in one statement. It creates new variables in the current scope. `=` is plain assignment — it assigns to an already-declared variable. A common mistake: using `:=` in a nested scope when you meant to assign to a variable in the outer scope, creating an unintended new shadow variable.
How do I use a variable declared in a type switch?
Variables declared in `switch v := x.(type)` case arms are scoped to that case. If you need the value outside the switch, declare the variable before the switch and assign inside: `var result string; switch v := x.(type) { case string: result = v }`.