← Debugging Guides

阅读中文版本

Go build fails with CGO_ENABLED=0 — cgo Code Not Available

Resolve cgo build failures during cross-compilation by isolating cgo files with build tags and choosing pure-Go implementations or a cross toolchain.

advanceddebugginggocgocross-compile

Symptoms

  • CGO_ENABLED=0 go build fails with "build constraints exclude all Go files" or missing C symbols
  • The build works on the host but fails for linux/arm64
  • A dependency such as a DNS or SQLite library requires cgo

Possible causes

  • A file imports "C" and has no pure-Go fallback, so disabling cgo excludes it
  • The cross compiler for the target architecture is not installed
  • The net package uses the cgo resolver, which is unavailable without cgo on some platforms

Diagnosis steps

Confirm cgo is the cause

CGO_ENABLED=0 go build ./... 2>&1 | head

Interpretation

Constraint-exclusion messages name the files that import "C" and lack a fallback.

Test a pure-Go build tag path

go build -tags netgo,osusergo ./...

Interpretation

Forcing pure-Go resolvers often lets a static cgo-free binary build.

Root cause

Disabling cgo removes every file that imports "C" from the build. If a package has no build-tagged pure-Go alternative, the target package ends up with no compilable files, and the linker cannot find C symbols that only cgo provided.

Fix

Provide a pure-Go implementation behind a build tag, select it with -tags, or build with cgo enabled and a proper cross toolchain (for example zig cc or a musl toolchain).

Risk note

Static cgo binaries need care with musl versus glibc; mixing them produces runtime loader errors.

Verify the fix

Build with CGO_ENABLED=0 and confirm a static binary; run file on it to confirm it is statically linked, then execute it on the target.

Symptoms

The error “Go build fails with CGO_ENABLED=0 — cgo Code Not Available” points at a specific failure mode. This guide documents the symptoms you can observe, the commands that isolate the cause, the root cause, the fix, and the checks that prove the fix works.

Observed symptoms include:

  • CGO_ENABLED=0 go build fails with “build constraints exclude all Go files” or missing C symbols
  • The build works on the host but fails for linux/arm64
  • A dependency such as a DNS or SQLite library requires cgo

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Confirm cgo is the cause

CGO_ENABLED=0 go build ./... 2>&1 | head

Constraint-exclusion messages name the files that import “C” and lack a fallback.

Step 2: Test a pure-Go build tag path

go build -tags netgo,osusergo ./...

Forcing pure-Go resolvers often lets a static cgo-free binary build.

Likely contributors, in order of frequency:

  • A file imports “C” and has no pure-Go fallback, so disabling cgo excludes it
  • The cross compiler for the target architecture is not installed
  • The net package uses the cgo resolver, which is unavailable without cgo on some platforms

Root Cause

Disabling cgo removes every file that imports “C” from the build. If a package has no build-tagged pure-Go alternative, the target package ends up with no compilable files, and the linker cannot find C symbols that only cgo provided.

The Fix

Provide a pure-Go implementation behind a build tag, select it with -tags, or build with cgo enabled and a proper cross toolchain (for example zig cc or a musl toolchain).

Watch out: Static cgo binaries need care with musl versus glibc; mixing them produces runtime loader errors.

// database_purego.go
//go:build !cgo

package store

func open(path string) (DB, error) { return openPure(path) }

// database_cgo.go
//go:build cgo

package store
/* #cgo LDFLAGS: -lsqlite3 */
import "C"

func open(path string) (DB, error) { return openCGO(path) }

Verify and Prevent

Build with CGO_ENABLED=0 and confirm a static binary; run file on it to confirm it is statically linked, then execute it on the target.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.