golang/go · error

${cmdline[0]}: ${err}

Error message

${cmdline[0]}: ${err}

What it means

Constructed in (*shell).run in cmd/go/internal/work/shell after a child process (compiler, linker, swig, pkg-config, etc.) exits with a non-zero status. The shell wraps the raw error (typically an *exec.ExitError like "exit status 1") by prefixing it with the invoked program's name, so the user can tell which tool failed. The message template is "<prog>: <underlying error>". This is the go command's standard way of attributing subprocess failures.

Source

Thrown at src/cmd/go/internal/work/shell.go:667

	start := time.Now()
	err = cmd.Run()
	if a != nil && a.json != nil {
		aj := a.json
		aj.Cmd = append(aj.Cmd, joinUnambiguously(cmdline))
		aj.CmdReal += time.Since(start)
		if ps := cmd.ProcessState; ps != nil {
			aj.CmdUser += ps.UserTime()
			aj.CmdSys += ps.SystemTime()
		}
	}

	// err can be something like 'exit status 1'.
	// Add information about what program was running.
	// Note that if buf.Bytes() is non-empty, the caller usually
	// shows buf.Bytes() and does not print err at all, so the
	// prefix here does not make most output any more verbose.
	if err != nil {
		err = errors.New(cmdline[0] + ": " + err.Error())
	}
	return buf.Bytes(), err
}

// joinUnambiguously prints the slice, quoting where necessary to make the
// output unambiguous.
// TODO: See issue 5279. The printing of commands needs a complete redo.
func joinUnambiguously(a []string) string {
	var buf strings.Builder
	for i, s := range a {
		if i > 0 {
			buf.WriteByte(' ')
		}
		q := strconv.Quote(s)
		// A gccgo command line can contain -( and -).
		// Make sure we quote them since they are special to the shell.
		// The trimpath argument can also contain > (part of =>) and ;. Quote those too.
		if s == "" || strings.ContainsAny(s, " ()>;") || len(q) > len(s)+2 {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Read the lines printed just above this error — the subprocess's stderr (buf.Bytes()) holds the real cause; the "<prog>: exit status N" line is just the attribution.
  2. Re-run the failing go command with -x to see the exact subprocess invocation, then run that command manually to reproduce.
  3. Fix the underlying compiler/linker/tool error reported in the captured output.
  4. If stderr was empty, check for signal kills (OOM), missing binaries (PATH), or permission issues on output paths.

Example fix

# before
$ go build ./...
# (no compiler output visible)
compile: exit status 1

# after — re-run with -x to see and reproduce the exact failing command
$ go build -x ./... 2>&1 | tail -20
$ /usr/local/go/pkg/tool/linux_amd64/compile -o /tmp/_o *.go   # reproduce & fix
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: ensure all tools the build will invoke exist and are executable.
func ensureTools(tools []string) error {
    for _, t := range tools {
        if _, err := exec.LookPath(t); err != nil {
            return fmt.Errorf("build tool %q not found in PATH: %w", t, err)
        }
    }
    return nil
}

Try / catch

// Always pair the wrapped error with captured stderr for context.
out, err := sh.run(...)
if err != nil {
    return fmt.Errorf("%s failed: %w\nstderr: %s", prog, err, out)
}

Prevention

When it happens

Trigger: Any subprocess the go command spawns returning non-zero: a compiler error (gc/gccgo), a linker failure, swig error, pkg-config error, asm failure, or a custom -gcflags/-ldflags tool. The wrapped error carries the program's argv[0]; buf.Bytes() (captured stderr) is usually shown by the caller instead of this string.

Common situations: Compilation errors (syntax, type errors). Missing include/library paths in CGo. Out-of-memory or signal kills during large builds. A tool invoked via -gcflags/-ldflags that does not exist or is not executable. Disk full or permission denied on output files.

Related errors


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