goseverity: can-fix
undefined: X

Go undefined: X — variable or symbol not found

undefined: X (variable or symbol not found)

99% fixable~5 mindifficulty: beginner

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.

> advertisementAdSense placeholder

How to fix it

  1. 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()
  2. 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)
  3. 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")
    }
  4. 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
  5. 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 }`.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is cross-referenced against the official sources listed in the “sources” sidebar before it ships. If a fix here didn’t work for you, please email so we can update the page.