Compiler & Linker Flags — Find the Bug¶
Common flag-related bugs with cause and fix.
Bug 1: -X injection doesn't work¶
Cause. -X only works on var declarations of type string. Constants and other types are ignored.
Fix.
Bug 2: -X injection silently misses¶
Cause. The flag's argument must be <package>.<var>=<value>. Without the package, the linker can't find the variable.
Fix.
For non-main packages: -X "example.com/pkg/buildinfo.Version=1.2.3".
Bug 3: Stripped binary refuses to debug¶
Cause. -s strips the symbol table; -w strips DWARF. Debuggers need both.
Fix. Build without stripping for debugging:
Or use a separate dev build target in your Makefile.
Bug 4: Stale build picks up old code¶
Cause. Build cache. Flags are part of the cache key. Changing -tags, -gcflags, etc., picks the right cache slot. But sometimes the binary is taken from a stale build directory.
Fix.
Usually the issue is that you're not running the binary you just built — check the path.
Bug 5: -race build fails on cross-compile¶
Cause. Race detector requires platform-specific runtime support; not all combinations are available.
Fix. Build -race only on the same architecture you'll run it. For CI, use a matching runner OS/arch.
Bug 6: PGO profile mismatched to code¶
Cause. Profiles capture function names and call patterns. After significant code changes, the profile may reference functions that no longer exist.
Fix. Refresh the profile from the current binary running representative load. The compiler still uses what matches and ignores the rest.
Bug 7: Cross-compiled cgo binary fails¶
Cause. Default CC=gcc can't cross-compile. cgo packages can't cross-build with the host compiler.
Fix. Either:
Or specify a cross-compiler:
Bug 8: -trimpath confuses an IDE¶
After building with -trimpath, your IDE's "go to source from stack trace" feature stops working.
Cause. -trimpath replaces local paths with package paths, which the IDE can't resolve to your specific clone.
Fix. Don't use -trimpath for local dev builds; reserve it for release.
Bug 9: Embedded default.pgo not picked up¶
Cause. -pgo=auto looks for default.pgo next to the main package directory. If your default.pgo is at the repo root, it's not found.
Fix. Move the file:
Or use an explicit path: -pgo=./default.pgo.
Bug 10: GOFLAGS interferes¶
go env GOFLAGS # -trimpath -tags=prod
go build ./...
# always builds with -trimpath and prod tag, unexpectedly
Cause. go env -w GOFLAGS=... was set previously and persists.
Fix.
Or override per-invocation:
Bug 11: Static link fails on glibc¶
go build -ldflags='-linkmode=external -extldflags="-static"' ./...
# /usr/lib/.../libc.so: error: cannot find -lc
Cause. Most glibc-based distros don't ship full static libs.
Fix. Build inside Alpine (musl) which does:
docker run --rm -v "$PWD:/src" -w /src golang:1.24-alpine \
sh -c 'apk add --no-cache build-base && go build -ldflags="-linkmode=external -extldflags=-static" ./...'
Bug 12: -buildvcs failure in CI¶
Cause. Some CI environments use shallow clones, detached HEADs, or read-only filesystems. Go's VCS info embedding fails or warns.
Fix. Disable explicitly:
Bug 13: -gcflags='-m' floods output¶
Cause. -m prints for every package in the build, including stdlib.
Fix. Constrain with a pattern:
Now only your packages are reported.
14. Summary¶
Flag bugs cluster around: silent failures (typo in -X target, missing package prefix), build cache confusion, cross-compilation toolchain mismatches, environment-variable surprises (GOFLAGS), and PGO file location. Each is preventable with a clear Makefile and CI configuration.
Further reading¶
go help build,go help buildflagsgo env: env-controlled defaults