Skip to content

Build and toolchain: the compiler errors

Level: 201 · anyone reading a go command message for the first time

One line: Some refusals come from the go command before the compiler runs — no module, a bad import path, a bad //go:embed pattern, a misplaced directive — and some from the compiler or the linker after it; each message below is Go 1.25's exact wording, recorded so that a later Go that rewords one shows up as a diff, and one entry is here because there is no message at all.

go.mod file not found

You ran go build . in a directory that has no go.mod, after go run main.go had worked in the same place.

Verified output of build_err_no_module_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go run main.go
orders: 3
exit status 0
$ go build -o orders main.go
exit status 0
$ go build -o orders .
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
exit status 1
$ go build ./...
pattern ./...: directory prefix . does not contain main module or its selected dependencies
exit status 1
$ go mod init example
go: creating new go.mod: module example
go: to add module requirements and sums:
    go mod tidy
$ go build -o orders . && ./orders
orders: 3
exit status 0

The mistake — go run main.go and go build main.go name files, which the go command compiles as a package of their own called command-line-arguments; . and ./... name packages, and a package lives in a module. The two messages differ because . is looked up as a package and ./... as a pattern.

The fix — a module, once:

go mod init example

package orders is not in std

You imported a package of your own module by its directory name instead of its import path.

Verified output of build_err_not_in_std_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -o report .
main.go:5:2: package orders is not in std (GOROOT/src/orders)
exit status 1
$ go build -o report . && ./report
orders: 3
exit status 0

The mistake — an import path with no dot and no module prefix is looked up in the standard library, and GOROOT/src/orders is the one place the go command looked (the script replaces the real GOROOT with the word). The module path in go.mod is the first part of every import path inside the module.

The fix

import "example/orders"

imported and not used, declared and not used

You left an import and a local variable behind while editing.

Verified output of build_err_unused_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -o report .
# example
./main.go:5:2: "os" imported and not used
./main.go:10:2: declared and not used: count
exit status 1

The mistake — both are compile errors in Go, not warnings, and the compiler reports every one it finds in a run. A package-level variable may go unused; a local one may not, and an import never.

The fix — remove them, or, while debugging, keep the compiler quiet on purpose:

_ = count

main redeclared in this block

You dropped a second file with a func main into the package directory, and then tried giving it another package name.

Verified output of build_err_main_redeclared_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -o report .
# example
./scratch.go:3:6: main redeclared in this block
    ./main.go:5:6: other declaration of main
exit status 1
$ go build -o report .
found packages main (main.go) and scratch (scratch.go) in $dir
exit status 1
$ rm scratch.go && go build -o report . && ./report
orders: 3
exit status 0

The mistake — every .go file in a directory that the build does not exclude is part of one package, so two files each declaring main collide, and the compiler names both places. A file with a different package clause does not fix that: the go command refuses a directory with two package names before compiling anything (found packages main (main.go) and scratch (scratch.go); the script replaces the directory with $dir).

The fix — one main per package. A scratch program goes in its own directory, or behind a constraint the build never satisfies, //go:build ignore:

//go:build ignore

package main

pattern missing.txt: no matching files found

You wrote a //go:embed pattern that names nothing, or something outside the package directory.

Verified output of build_err_embed_patterns_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -o report .   # //go:embed missing.txt
main.go:8:12: pattern missing.txt: no matching files found
exit status 1
$ go build -o report .   # //go:embed ../VERSION
main.go:8:12: pattern ../VERSION: invalid pattern syntax
exit status 1
$ go build -o report .   # //go:embed /etc/hosts
main.go:8:12: pattern /etc/hosts: invalid pattern syntax
exit status 1
$ go build -o report .   # //go:embed "VERSION
# example
./main.go:8:3: invalid quoted string in //go:embed: "VERSION
./main.go:8:3: usage: //go:embed pattern...
exit status 1
$ go build -o report .   # //go:embed VERSION
exit status 0

The mistake — a pattern is matched against the package directory when the package is built, so a file that is not there yet, or that a generator will write later, matches nothing; .. and an absolute path are invalid pattern syntax, because an embedded file must live under the package directory; an unterminated quoted string is a syntax error in the directive itself. These come from the go command, not the compiler — no # example line above them.

The fix — the file in the package directory, and the pattern relative to it:

//go:embed VERSION
var version string

go:embed cannot apply to var inside func

You put a //go:embed directive where the compiler cannot honour it.

Verified output of build_err_embed_placement_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -o report .   # the var is inside a function
# example
./main.go:9:4: go:embed cannot apply to var inside func
exit status 1
$ go build -o report .   # nothing imports embed
# example
./main.go:5:3: go:embed requires import "embed" (or import _ "embed", if package is not used)
exit status 1
$ go build -o report .   # the var has an initialiser
# example
./main.go:8:3: go:embed cannot apply to var with initializer
exit status 1
$ go build -o report .   # the var is an int
# example
./main.go:9:5: go:embed cannot apply to var of type int
exit status 1

The mistake — four rules, one message each: the variable is package-level, the file imports embed (blank-imported if only string or []byte are used), the variable has no initialiser, and its type is string, []byte or embed.FS (//go:embed puts files in the binary).

The fix

import _ "embed"

//go:embed VERSION
var version string

cannot set with -X: not a var of type string

You passed -ldflags "-X …" for a variable that is not a string, or wrote the flag in the wrong shape — and once for a variable that does not exist.

Verified output of build_err_x_flag_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -ldflags "-X main.build=7" -o report .
# example
main.build: cannot set with -X: not a var of type string (type:int)
exit status 1
$ go build -ldflags "-X version=1.4.2" -o report .
# example
link: -X flag requires argument of the form importpath.name=value
exit status 1
$ go build -ldflags "-X main.Version=1.4.2" -o report . && ./report
version="dev" build=0
exit status 0
$ go build -ldflags "-X main.version=1.4.2" -o report . && ./report
version="1.4.2" build=0
exit status 0

The mistake — -X writes into the storage of a string variable, so an int is refused with its type named, and an argument without the importpath.name=value shape is refused by the linker itself (the message begins with the linker's path, replaced here by link:). The third build is the one to remember: main.Version names nothing — the variable is version — and the link succeeds, exit status 0, with the program still saying dev. There is no message for a misspelt name (-ldflags -X sets a variable at link time).

The fix — the exact name, package path first, and a string on the other side:

go build -ldflags "-X main.version=1.4.2" -o report .

misplaced compiler directive

You put a //go:build line after the package clause, wrote two of them, or left an expression half-typed.

Verified output of build_err_directives_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ go build -o report .   # //go:build after the package clause
# example
./pricing_fast.go:3:3: misplaced compiler directive
exit status 1
$ go build -o report .   # two //go:build lines
pricing_fast.go: multiple //go:build comments
exit status 1
$ go build -o report .   # an expression that does not parse
pricing_fast.go: parsing //go:build line: unexpected end of expression
exit status 1
$ go build -o report .
exit status 0

The mistake — a //go:build line is honoured only before the package clause; after it, the compiler sees a directive it cannot apply and refuses. A second line and a malformed expression are refused earlier, by the go command while it decides which files to build, so those two messages carry no # example header. Vet's buildtag analyzer reports the first case too (what vet and gofmt catch).

The fix — one line, first, with a blank line after it:

//go:build fast && !debug

package main