golang/go · error

%s requires external (cgo) linking, but cgo is not enabled

Error message

%s requires external (cgo) linking, but cgo is not enabled

What it means

Returned by LinkerDeps at line 2687. externalLinkingReason (line 2714) decides the final binary must use external (cgo) linking — because the target platform always requires it (platform.MustLinkExternal, e.g. some os/arch combos), because of -buildmode=c-shared/plugin, because of -linkshared, because PIE on a platform without internal-link PIE support, or because -ldflags=-linkmode=external was passed. If cgo is then disabled (cfg.BuildContext.CgoEnabled false, and compiler != gccgo), runtime/cgo cannot be pulled in and the build aborts. gccgo is exempt because it links externally natively.

Source

Thrown at src/cmd/go/internal/load/pkg.go:2683

// We accept leading . _ and / as likely in file system paths.
// There is a copy of this function in cmd/compile/internal/noder/noder.go.
func SafeArg(name string) bool {
	if name == "" {
		return false
	}
	c := name[0]
	return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
}

// LinkerDeps returns the list of linker-induced dependencies for main package p.
func LinkerDeps(s *modload.Loader, p *Package) ([]string, error) {
	// Everything links runtime.
	deps := []string{"runtime"}

	// External linking mode forces an import of runtime/cgo.
	if what := externalLinkingReason(s, p); what != "" && cfg.BuildContext.Compiler != "gccgo" {
		if !cfg.BuildContext.CgoEnabled {
			return nil, fmt.Errorf("%s requires external (cgo) linking, but cgo is not enabled", what)
		}
		deps = append(deps, "runtime/cgo")
	}
	// On ARM with GOARM=5, it forces an import of math, for soft floating point.
	if cfg.Goarch == "arm" {
		deps = append(deps, "math")
	}
	// Using the race detector forces an import of runtime/race.
	if cfg.BuildRace {
		deps = append(deps, "runtime/race")
	}
	// Using memory sanitizer forces an import of runtime/msan.
	if cfg.BuildMSan {
		deps = append(deps, "runtime/msan")
	}
	// Using address sanitizer forces an import of runtime/asan.
	if cfg.BuildASan {
		deps = append(deps, "runtime/asan")

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Enable cgo: set CGO_ENABLED=1 and ensure a C compiler (gcc/clang) is on PATH for the target.
  2. Remove the constraint that forces external linking: drop -buildmode=c-shared/plugin/pie, drop -linkshared, or drop -ldflags=-linkmode=external.
  3. If you must ship a static binary, switch to a target/platform that supports internal linking and drop the external-linking buildmode.
  4. On gccgo the check is skipped, but switching compiler is rarely the right fix — prefer enabling cgo.

Example fix

# before
CGO_ENABLED=0 go build -buildmode=pie ./cmd/app
# after
CGO_ENABLED=1 go build -buildmode=pie ./cmd/app   # or drop -buildmode=pie
Defensive patterns

Strategy: validation

Validate before calling

// Detect the conflict up front: if any external-linking-forcing flag is set,
// CGO must be enabled. Mirror externalLinkingReason's decision inputs.
package buildcheck

import (
	"errors"
	"os"
	"strings"
)

// platforms that always require external linking; consult cmd/go's
// platform.MustLinkExternal for the authoritative list.
var mustLinkExternal = map[string]bool{
	"android/arm": true, // etc.
}

func CgoCompatibleWithFlags(goos, goarch, buildmode, ldflags string) error {
	cgoEnabled := os.Getenv("CGO_ENABLED") != "0"
	forcesExternal := false
	switch buildmode {
	case "c-shared", "plugin":
		forcesExternal = true
	case "pie":
		forcesExternal = true // conservative; depends on InternalLinkPIESupported
	}
	if strings.Contains(ldflags, "-linkmode=external") {
		forcesExternal = true
	}
	if mustLinkExternal[goos+"/"+goarch] {
		forcesExternal = true
	}
	if forcesExternal && !cgoEnabled {
		return errors.New("selected build flags require external (cgo) linking; set CGO_ENABLED=1")
	}
	return nil
}

Try / catch

// If a scripted build fails with this message, retry with cgo enabled:
//
//   cmd := exec.Command("go", "build", flags...)
//   if out, err := cmd.CombinedOutput(); err != nil &&
//      bytes.Contains(out, []byte("requires external (cgo) linking")) {
//       cmd = exec.Command("go", append([]string{"env", "-w", "CGO_ENABLED=1"})...)
//       _ = cmd.Run()
//   }

Prevention

When it happens

Trigger: Building with CGO_ENABLED=0 while any of: targeting a platform in platform.MustLinkExternal; -buildmode=c-shared or -plugin (non-wasm); -linkshared; -buildmode=pie on a platform lacking InternalLinkPIESupported; or -ldflags=-linkmode=external.

Common situations: Docker `CGO_ENABLED=0` scratch images combined with `-buildmode=pie` or `-buildmode=c-shared`; cross-compiling to darwin/arm64 or android with cgo off; CI hardening setting PIE defaults while forcing static binaries; passing -linkshared without a cgo-enabled toolchain.

Related errors


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