Fix

"build constraints exclude all Go files"

Fix the 'build constraints exclude all Go files' error by ensuring at least one file in the package lacks build constraints that exclude your current OS or architecture.

When Go refuses to see your code

You clone a repository from a colleague's Linux server. You open your terminal on your Mac and run go run main.go. The command fails instantly with build constraints exclude all Go files. You check the directory. main.go is right there. You didn't change anything. Go claims the package is empty.

This error means the compiler looked at every file in the directory, checked the build tags on each one, and decided that none of them should compile on your current system. Go treats the package as if it contains zero files, and a package with no files cannot build.

What build constraints actually do

Build constraints are boolean expressions written as comments at the top of a Go file. They tell the compiler whether to include that file in the build. Go evaluates these constraints before it parses the code. If the constraint evaluates to false for your operating system or architecture, the file becomes invisible to the compiler.

Think of build constraints like a filter on a media player. You have a folder of video files, each tagged with the device it supports. If you plug the player into a car, it only shows files tagged for the car display. If every file in the folder is tagged for a TV, the player shows nothing. Go does the same thing. It scans the directory, checks the tags, and skips files that don't match your environment.

The minimal case

A single line of comment can hide a file from the compiler.

//go:build linux

package main

func main() {
    println("This only runs on Linux")
}

If you run this file on macOS, the compiler skips it entirely. If this is the only file in the package, the build fails with build constraints exclude all Go files. The constraint linux is true only when the target OS is Linux. On any other system, the file is excluded.

How the compiler evaluates constraints

The go tool reads the first few lines of every .go file in a package. It looks for comments starting with //go:build. It parses the expression and evaluates it against the current build environment, which is defined by environment variables like GOOS and GOARCH.

If the expression is true, the file is included. If it is false, the file is skipped. If all files are skipped, the compiler stops and reports the error. The evaluation happens per-file. A package can have multiple files with different constraints. The compiler assembles the package from whatever files pass the filter.

Convention aside: The //go:build line must be followed by a blank line before the package clause. This is a hard rule of the parser. If you omit the blank line, the compiler ignores the constraint entirely. The file compiles everywhere, which defeats the purpose and often causes subtle bugs where OS-specific code runs on the wrong system.

//go:build linux

package main

// The blank line above is required. Without it, the constraint is ignored.

Real-world patterns

Build constraints shine when you need to provide different implementations for different platforms. A common pattern is to split platform-specific logic into separate files.

//go:build linux

package sys

// GetHostname returns the hostname on Linux using the syscall package.
func GetHostname() string {
    name, _, _ := syscall.RawSyscall(syscall.SYS_GETDOMAINNAME, ...)
    return string(name)
}
//go:build !linux

package sys

// GetHostname returns the hostname on non-Linux systems using the os package.
func GetHostname() string {
    host, _ := os.Hostname()
    return host
}

In this package, the compiler includes exactly one file. On Linux, it picks the first file. On macOS or Windows, it picks the second. The package compiles cleanly on all systems. If you accidentally add //go:build linux to the second file, the package breaks on non-Linux systems because both files are excluded.

Constraint syntax and operators

Constraints support logical operators. You can combine conditions to target specific environments.

  • linux || darwin matches Linux or macOS.
  • !windows matches everything except Windows.
  • amd64 && linux matches 64-bit Linux systems.
  • (linux || darwin) && amd64 matches 64-bit Linux or macOS.

The syntax uses standard boolean logic. Parentheses control precedence. The ! operator negates a condition. The || operator is OR. The && operator is AND.

You can also use the legacy // +build syntax, but //go:build is the modern standard. The legacy syntax has limitations and is harder to read. New code should always use //go:build.

Common pitfalls

The most common mistake is copy-pasting a constraint without updating it. You copy a Linux-specific file to create a macOS version, but you forget to change the constraint. The new file still says //go:build linux. On macOS, both files are excluded. The build fails.

Another trap is the ignore constraint. The tag //go:build ignore tells the compiler to skip the file in all environments. This is useful for example files or test data that should never compile. If you accidentally add this to main.go, the build fails with build constraints exclude all Go files. The compiler sees the file, reads the constraint, evaluates it to false, and skips it.

Cross-compilation can also trigger this error. If you run GOOS=linux go build on a Mac, the compiler evaluates constraints as if it were building for Linux. If your package has files with //go:build darwin and no files for Linux, the build fails. The error message is the same, but the cause is the environment variable override.

Convention aside: Run go env to check your current build environment. The output shows GOOS and GOARCH. This is the source of truth for how the compiler evaluates constraints. If you suspect a mismatch, compare the output of go env with the constraints in your files.

Debugging with go list

When the error occurs, the fastest way to diagnose it is to ask the compiler which files it is considering. The go list command can show you the files included in a package.

Run go list -f '{{.GoFiles}}' ./your/package. This prints the list of files that pass the build constraints. If the output is empty, every file is excluded. You can then check each file for constraints that don't match your environment.

You can also force the compiler to show constraints by running go list -f '{{.GoFiles}} {{.IgnoredGoFiles}}' ./your/package. This prints both the included files and the ignored files. The ignored list helps you spot files that are being skipped due to constraints.

Decision matrix

Use //go:build os when you need code that depends on OS-specific syscalls, libraries, or behavior. Use //go:build !os when you need a fallback implementation for all systems except one. Use //go:build ignore when a file contains documentation examples or test data that should never compile. Use no constraints when the code is portable across all supported platforms. Use GOOS and GOARCH environment variables when you need to cross-compile for a different target.

Constraints are filters. If the filter blocks everything, Go stops.

Where to go next