golang/go · error

flag %q triggers external linking

Error message

flag %q triggers external linking

What it means

Thrown by checkCompilerFlagsForInternalLink when a compiler flag begins with `-flto` (link-time optimization) while the build is attempting internal (Go-only) linking. LTO embeds IR that only the external system linker can resolve, so the go command refuses it rather than silently falling back.

Source

Thrown at src/cmd/go/internal/work/security.go:339

	return checkFlags(name, source, list, nil, validPkgConfigFlags, nil, checkOverrides)
}

// checkCompilerFlagsForInternalLink returns an error if 'list'
// contains a flag or flags that may not be fully supported by
// internal linking (meaning that we should punt the link to the
// external linker).
func checkCompilerFlagsForInternalLink(name, source string, list []string) error {
	checkOverrides := false
	if err := checkFlags(name, source, list, nil, validCompilerFlags, validCompilerFlagsWithNextArg, checkOverrides); err != nil {
		return err
	}
	// Currently the only flag on the allow list that causes problems
	// for the linker is "-flto"; check for it manually here.
	// Also check for -static/--static, which some toolchains accept
	// as a compiler flag.
	for _, fl := range list {
		if strings.HasPrefix(fl, "-flto") {
			return fmt.Errorf("flag %q triggers external linking", fl)
		}
		if fl == "-static" || fl == "--static" {
			return fmt.Errorf("flag %q triggers external linking", fl)
		}
	}
	return nil
}

// checkLinkerFlagsForInternalLink returns an error if 'list'
// contains linker flags that are not compatible with internal linking.
func checkLinkerFlagsForInternalLink(name, source string, list []string) error {
	checkOverrides := false
	if err := checkFlags(name, source, list, nil, validLinkerFlags, validLinkerFlagsWithNextArg, checkOverrides); err != nil {
		return err
	}
	// Flags that force static linking require the external linker
	// to resolve libc symbols. See #77768.
	for _, fl := range list {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Remove `-flto` from CGO_CFLAGS / the #cgo CFLAGS line and rebuild.
  2. Allow external linking: add `-buildmode=pie` or build with cgo so the external linker is used (the flag is only blocked for internal linking).
  3. Override the inherited CFLAGS: `CGO_CFLAGS='' go build`.
  4. Find the source: `go build -x` prints the offending flag and which #cgo block contributed it.

Example fix

// before
// #cgo CFLAGS: -O2 -flto

// after
// #cgo CFLAGS: -O2
Defensive patterns

Strategy: validation

Validate before calling

// Strip -flto* from CGO_CFLAGS before building
cflags := os.Getenv("CGO_CFLAGS")
filtered := cflags[:0]
for _, f := range strings.Fields(cflags) {
    if !strings.HasPrefix(f, "-flto") {
        filtered = append(filtered, f)
    }
}
os.Setenv("CGO_CFLAGS", strings.Join(filtered, " "))

Prevention

When it happens

Trigger: Build a pure-Go or cgo package with internal linking while CGO_CFLAGS / CGO_LDFLAGS / a #cgo CFLAGS line contains `-flto`, `-flto=auto`, `-flto=thin`, etc. The HasPrefix("-flto") branch in checkCompilerFlagsForInternalLink returns.

Common situations: Inheriting distro CFLAGS (`export CFLAGS=-flto`) that bleed into CGO_CFLAGS; a vendored C dependency that hard-codes `-flto` in its #cgo directive; CI that sets aggressive optimization env vars globally.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/e9a9ea6a1d140b89. Report an issue: GitHub.