Undefined Name Errors in Go and Where They Come From
undefined means the compiler cannot find a name in scope. Check typos, imports, scope, unexported names, files left out of the build and build tags.

undefined: something is the compiler's way of saying it looked everywhere it was allowed to look and found nothing by that name. It is a short message with at least eight distinct causes, and the order in which you check them matters, because the first three account for most of the cases. This article lists them in that order, with the actual output, and ends with the build-system causes that fool people who are certain the function exists. It is related to declared and not used, which often appears beside it, and to packages and exported names, which explains the visibility rule behind one of the causes. It belongs in the Go basics path.
Tested with Go 1.25.6, macOS on Apple Silicon (arm64)
The first four causes, in one file
This file has a typo, a missing import, a wrong scope and a name that was never declared:
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(helper(2))
fmt.Println(Count)
fmt.Println(strings.Toupper("x"))
fmt.Println(fmt.println("y"))
for i := 0; i < 2; i++ {
total := i
}
fmt.Println(total)
fmt.Println(os.Args)
var s Server
s.Start()
}# example.test/lab
./main.go:9:14: undefined: helper
./main.go:10:14: undefined: Count
./main.go:11:22: undefined: strings.Toupper (but have ToUpper)
./main.go:12:18: undefined: fmt.println (but have Println)
./main.go:14:3: declared and not used: total
./main.go:16:14: undefined: total
./main.go:17:14: undefined: os
./main.go:18:8: undefined: ServerEach error names the first thing the compiler could not resolve. Reading the output top to bottom gives the checklist.
Nothing by that name exists. undefined: helper, undefined: Count, undefined: Server. The name is not declared in this function, in this package's files that are part of the build, or in the universe of builtins. Either it was never written, it was misspelled, or it lives where the compiler is not looking, which the rest of this article covers.
A case typo. undefined: strings.Toupper (but have ToUpper). Modern compilers search for a name that differs only in capitalization and tell you. The same for fmt.println, which exists as Println and must start with a capital to be reachable from another package. If the hint is there, believe it.
A scope mistake. total was declared inside the for body with :=, so after the closing brace it does not exist, and the next line reports undefined: total. The compiler also complained that total was declared and not used, which is the same mistake seen from the inside. The fix is to declare the variable before the loop, total := 0, and assign inside it with =.
A missing import. undefined: os means the code uses os.Args and no os import is present. This is the one editors fix automatically: goimports or gopls adds the import on save. In a bare editor the fix is a line in the import block. The reverse mistake, an import you stop using, is a different error, as the hello world article shows.
Unexported names in other packages
Calling something that exists, but is private to its package, gives a variant of the same family. For a function that starts with a lowercase letter, the message is undefined: pkg.name, or, depending on the situation, name not exported by package, both described in packages and exported names. The two-line fix is to export the name (capitalize it) or to add an exported wrapper. If the name is in someone else's module, it is private on purpose, and you need a different API.
A related message is x.y undefined (type T has no field or method y). That one says the identifier is fine and the type does not have the member. Check the spelling and capitalization again, then check whether the method has a pointer receiver and the variable is a value of an interface type that does not include it.
The file you did not compile
The cause that wastes the most time is a function that is defined, in a file the build does not include. Two causes:
go run main.go. With a file name argument, the command compiles only the files you list. A function in a sibling file is invisible:
package main
import "fmt"
func main() {
fmt.Println(other())
}# command-line-arguments
./main.go:6:14: undefined: otherpackage main
func other() string { return "from util.go" }The build says undefined: other, though util.go is right there in the directory. The fix is to run the package, not the file. In the same directory, go run . compiles every file:
go run .from util.goThat command printed from util.go here. This is why your first program recommends go run . as the habit: it matches what go build does and does not depend on which files you remember to list.
Build constraints. A file whose name ends in _windows.go, _linux.go, _arm64.go and so on, or that has a //go:build line, is included only on matching platforms. A function defined only there is undefined on other platforms. Here the helper is declared in a Windows-only file:
package main
import "fmt"
func main() {
fmt.Println(platform())
}# example.test/lab
./main.go:6:14: undefined: platformpackage main
func platform() string { return "windows" }On this macOS machine the compiler cannot see platform. The error reads undefined: platform once more, and nothing in it mentions the platform file, which is why this one is hard to spot. The fix is a file for each platform that defines the same function, or a shared default with a build constraint, as described in build tags and conditional compilation and OS and architecture file suffixes. Check with go list -f '{{.GoFiles}} {{.IgnoredGoFiles}}' ., which lists the files that were included and the ones that were ignored.
Module errors that look like undefined
An import of a package that is not in your module or its dependencies gives a different, longer message:
main.go:3:8: no required module provides package example.test/lab/missing; to add it:
go get example.test/lab/missingI produced that by importing a package that does not exist in a fresh module. It is a module problem and not a scope problem. The compiler never got as far as looking for names. The fix is the command it prints, go get, for a third-party package, or go mod tidy, which adds every missing requirement and removes the unused ones. If the import is a package inside your own module, check the path: it is the module name from go.mod plus the directory, and a typo there gives the same message. The module basics are in how GOPATH gave way to Go modules.
A quick order of checks
When you meet undefined, ask in this order and stop at the first yes. Is the spelling and capitalization right, and does the message offer a but have? Is the package imported? Is the name declared in an enclosing scope, or only inside a block that ended? Is it unexported in another package? Are you running a single file with go run file.go? Does the defining file have a platform suffix or build tag that excludes it? Is it a generics or method-set problem, where the name exists on a type you are not holding? Most causes die at the first three questions, and the last two are the ones that need a command.
When the tool says something different from the editor
If the editor underlines a name and go build accepts it, or the reverse, the editor and the build are looking at different configurations. The usual reason is a build tag or a workspace setting, for example gopls analyzing for linux while you build for darwin. Set the same GOOS, GOARCH and -tags in the editor's environment. And if you recently moved files between packages, run go build ./... and not just the package you edited, so every importer gets rechecked. The specification on scope defines the rules the compiler applies, and the go build constraints documentation lists every way a file can be excluded.


