Build Constraints — Find the Bug¶
Realistic build-constraint bugs with cause and fix.
Bug 1: Missing blank line¶
Symptom. Build succeeds on macOS even though mypkg should be Linux-only.
Cause. The //go:build line is treated as a doc comment for package because there's no blank line between them.
Fix.
gofmt enforces this — running it would have caught the bug.
Bug 2: Typo in OS name¶
Symptom. File never compiles, no error.
Cause. Go uses darwin, not osx. Unknown tag names are simply treated as false; there's no diagnostic.
Fix.
When in doubt, list ignored files:
Bug 3: Both implementations included¶
Symptom. Build error: "Hash redeclared".
Cause. Both files contribute Hash on Linux. The second file has no constraint, so it's always built.
Fix. Add //go:build !linux to crypto_b.go.
Bug 4: Neither implementation included¶
Symptom. "undefined: Hash" when building on macOS.
Cause. Neither file's constraint matches on Darwin. The partition isn't complete.
Fix. Add a fallback:
// crypto_other.go
//go:build !linux && !windows
package crypto
func Hash() []byte { /* portable */ }
Or use //go:build unix etc. to widen coverage.
Bug 5: Legacy +build out of sync¶
Symptom. Code compiles on amd64 with newer Go (which prefers //go:build), but on older Go reads the +build line and tries to build for arm64.
Cause. The two lines disagree. gofmt would normally keep them in sync — they likely got out of sync via manual edit.
Fix. Drop the +build line entirely if pre-1.17 support isn't needed:
If you must keep both, run gofmt -w to align them.
Bug 6: Tag never set¶
Symptom. experimental_feature.DoIt is unused lint warning. Or worse, callers can't find it.
Cause. Nobody runs go build -tags=experimental. The file is excluded from all builds.
Fix. Either:
- Document the tag clearly in the README ("set
-tags=experimentalto enable"). - Remove the tag if the feature is ready for general use.
Bug 7: Test that never runs¶
Symptom. The test never fails because CI never runs it.
Cause. No CI job sets -tags=integration.
Fix. Add a CI job:
Or remove the tag if the test should run in the default suite.
Bug 8: runtime.GOOS instead of build tag¶
package fs
func Walk(root string) error {
if runtime.GOOS == "windows" {
return errors.New("not supported on Windows")
}
return unixWalk(root)
}
Symptom. Compile fails on Windows: "undefined: unixWalk".
Cause. Mixing runtime checks with platform-only functions. The Windows build tries to compile unixWalk regardless of the runtime check.
Fix. Split into files:
// fs_windows.go
//go:build windows
package fs
func Walk(root string) error { return errors.New("not supported") }
Bug 9: Vet skips constrained files¶
But Linux-only files have bugs that aren't reported on macOS.
Cause. go vet only inspects files in the active build context.
Fix. Run vet across all supported platforms in CI:
Bug 10: gopls errors after switching branches¶
You switch to a branch that uses different tags. Your editor lights up with red squiggles.
Cause. gopls is configured for the old tag set.
Fix. Update .vscode/settings.json or .gopls/settings.json:
Restart the language server.
Bug 11: Constraint inside a test file ignored¶
go test ./... doesn't run TestSlow. But neither does go test -tags=slow ./....
Cause. The first issue is that there's a missing blank line. The second: the file is still constrained even when tags match — if your go test doesn't include the tag, the file is skipped. Tests do not run by default just because they're in a _test.go file with a //go:build line.
Fix. Use t.Skip() for runtime-dependent skipping; reserve tags for "this file is excluded from the build".
Bug 12: Custom tag picked up unexpectedly¶
You meant integration and experimental as two tags. Shell quoted, they look like a single space-separated string.
Cause. Spaces split -tags into multiple tag names. Sometimes the shell or build tool collapses them; sometimes they're parsed as one.
Fix. Always use commas:
Bug 13: File-name suffix not what you think¶
Symptom. File compiles on all platforms.
Cause. _linuxamd64 isn't a recognized suffix. The runtime recognizes _GOOS_GOARCH (with an underscore separator).
Fix. Rename:
14. Summary¶
Build-constraint bugs cluster around: missing blank lines, OS-name typos, incomplete partitions, mixed runtime/build approaches, untagged CI commands, and editor configuration. The diagnostic tool — go list -e -f '{{.IgnoredGoFiles}}' — finds most of them in seconds. CI matrix builds catch the rest.
Further reading¶
go help buildconstraintgofmt -wfor syncing legacy/modern tagsgoplssettings: https://github.com/golang/tools/blob/master/gopls/doc/settings.md