---
name: build-errors-go
description: >
  Resolución de errores de compilación Go: go build, go vet, go mod, import
  cycles, cgo, race conditions y linters estáticos. Cargar cuando un build Go
  falle o haya errores de compilación, módulos o vet.
version: "1.0.0"
herramientasPermitidas: [Read, Bash, Grep]
exclusiones:
  - "No cargar para errores de lógica o comportamiento del programa Go en ejecución — si `go build` pasa pero el programa produce resultados incorrectos, es un bug de lógica; no es un error de compilación."
  - "No cargar para errores de pruebas que fallan por assertions incorrectas (testify, gomock) — si `go test` compila pero los tests fallan por valores incorrectos, eso es un error de lógica de tests, no de build."
  - "No cargar para diseñar la arquitectura de módulos Go o decidir la estructura de packages — si la pregunta es cómo organizar los paquetes del proyecto, cargar `go-patrones` o `go-experto`."
  - "No cargar cuando el error es exclusivo de cgo y el proyecto embebe C — los errores de cgo tienen dependencias del compilador C y son un dominio separado que requiere conocimiento de C además de Go."
evolvable: true  # default para skill estandar
---
# Build Errors Go — Diagnóstico y Solución

Go es estricto en tiempo de compilación: variables no usadas, imports no usados
y type mismatches son errores, no warnings. Este skill cubre los errores más
frecuentes del compilador, del sistema de módulos y de las herramientas de
análisis estático.

## Cuándo NO cargar

- `go build` compila pero el programa falla en ejecución — no es un error de build; diagnosticar con `-race` o logs.
- Los tests fallan por valores incorrectos (assertion failure) aunque compilan — es un error de lógica, no de compilación.
- La pregunta es cómo estructurar paquetes o módulos Go — para diseño de paquetes cargar `go-patrones`.

## Cuándo cargar este skill

Invoca `Skill("build-errors-go")` cuando:

- `go build` o `go test` falle con errores de compilación
- `go vet` reporte problemas
- Haya errores de `go mod` (módulos, versiones, replace)
- Se detecten import cycles
- `go build -race` detecte race conditions
- Linters como `staticcheck` o `golangci-lint` fallen en CI

## Conceptos clave

### Errores vs Warnings en Go

Go no tiene warnings — todo es un error que impide la compilación. Un import
sin usar o una variable declarada sin usar genera un error de compilación.
Esto es intencional: mantiene el código limpio por construcción.

### Go Modules (go.mod)

Cada módulo tiene un `go.mod` que declara el module path y las dependencias.
`go.sum` contiene los checksums criptográficos de cada dependencia. Ambos
se versionan en git. Las dependencias se cachean en `$GOPATH/pkg/mod`.

### Build Constraints

Los build constraints (`//go:build linux`) condicionan la compilación por OS,
arquitectura u otras condiciones. Un archivo `.go` que no cumple los constraints
del ambiente actual se ignora completamente, lo que puede causar errores de
"undefined" si la implementación está en el archivo excluido.

## Errores de go build — causa y solución

### `undefined: NombreFuncion` o `undefined: paquete.Tipo`

**Causa 1**: La función/tipo no está en el paquete importado.
```bash
# Verificar que existe en la version del modulo que se usa
go doc paquete.Funcion
```

**Causa 2**: La función está en un archivo con build constraint excluido.
```bash
# Ver que archivos se incluyen en el build actual
go list -f '{{.GoFiles}}' ./paquete/
go list -f '{{.IgnoredGoFiles}}' ./paquete/
```

**Causa 3**: Import cycle impide que el paquete compile.
```bash
go build ./... 2>&1 | grep "import cycle"
```

### `declared but not used` (variable sin usar)

```go
// MAL — Go no compila con variables sin usar
func calcular() int {
    resultado := 42  // ERROR si no se usa
    return 0
}

// BIEN — usar la variable o usar _ si el valor no importa
func calcular() (int, error) {
    resultado, err := operacionConError()
    if err != nil {
        return 0, err
    }
    return resultado, nil
}

// Para ignorar un valor de retorno explícitamente:
_, err := operacion()
```

### `imported and not used: "paquete"`

```go
// MAL — import que Go no acepta si no se usa
import (
    "fmt"
    "os"   // ERROR si no se llama ningún símbolo de "os"
)

// BIEN — remover el import no usado
// O usar goimports para limpiar automáticamente:
// goimports -w archivo.go
```

### `cannot use X (type T1) as type T2`

```go
// Causa común: tipos nombrados no son intercambiables aunque tengan el mismo tipo base
type MetrosCuadrados float64
type Kilogramos float64

var area MetrosCuadrados = 10.0
var peso Kilogramos = area  // ERROR: tipos distintos aunque ambos son float64

// Solución: conversión explícita
var peso Kilogramos = Kilogramos(area) // solo si la conversión tiene sentido semántico
```

### Import cycle — `import cycle not allowed`

**Causa**: Paquete A importa B, B importa A (directamente o transitivamente).

**Diagnóstico**:
```bash
go build ./... 2>&1
# Output: package miapp/pkgA: import cycle not allowed
# miapp/pkgA -> miapp/pkgB -> miapp/pkgA
```

**Soluciones en orden de preferencia**:
1. Extraer los tipos compartidos a un tercer paquete que ninguno de los dos importa
2. Usar una interface en lugar de un tipo concreto para romper el ciclo
3. Reestructurar los paquetes para eliminar la dependencia circular

```
# Estructura que crea ciclo:
pkgA imports pkgB
pkgB imports pkgA

# Solucion: extraer tipos compartidos
pkgTypes (tipos compartidos, no importa a nadie)
pkgA imports pkgTypes
pkgB imports pkgTypes
```

## Errores de go mod — causa y solución

### `no required module provides package X`

```bash
# Agregar el modulo faltante
go get github.com/paquete/necesitado@v1.2.3

# O si es un subpaquete, agregar el modulo raiz
go get github.com/paquete/raiz
```

### `ambiguous import: found package X in multiple modules`

**Causa**: Dos módulos exponen el mismo import path. Ocurre en fork/rename.

```bash
# Identificar cuáles módulos proveen el path
go mod why github.com/path/en/conflicto
```

**Solución con replace en go.mod**:
```
replace github.com/paquete/original => github.com/fork/alternativo v1.0.0
```

### `go: updates to go.sum needed; to update it: go mod tidy`

```bash
# Regenerar go.sum para que coincida con go.mod
go mod tidy

# Si hay errores de checksum:
go clean -modcache
go mod download
```

### `verifying X: checksum mismatch`

**Causa**: El checksum en `go.sum` no coincide con lo descargado. Posible
modificación del módulo upstream o corrupción de cache.

```bash
# Limpiar cache y re-descargar
go clean -modcache
GONOSUMCHECK=* go mod download   # solo para diagnostico, no en produccion
go mod verify
```

## Errores de go vet — causa y solución

### `printf: format %s has arg X of wrong type`

```go
// MAL — tipo incorrecto para el formato
var n int = 42
fmt.Printf("%s", n)  // vet error: %s espera string, se paso int

// BIEN
fmt.Printf("%d", n)
```

### `copylocks: X passes lock by value`

```go
// MAL — Mutex no debe pasarse por valor (se copia el estado)
type MiStruct struct {
    mu sync.Mutex
}

func procesar(s MiStruct) { ... }       // ERROR: copia el Mutex

// BIEN — pasar por puntero
func procesar(s *MiStruct) { ... }
```

## Race conditions — `go build -race`

```bash
# Compilar y ejecutar con detector de race conditions
go test -race ./...
go run -race main.go
```

**Patrón común detectado**:
```go
// MAL — acceso concurrente sin sincronización
var contador int
go func() { contador++ }()
go func() { contador++ }()
// DATA RACE: dos goroutines escriben contador sin mutex

// BIEN — usar sync.Mutex o atomic
var mu sync.Mutex
go func() { mu.Lock(); contador++; mu.Unlock() }()
// O mejor con sync/atomic:
var contador int64
go func() { atomic.AddInt64(&contador, 1) }()
```

## Errores de staticcheck comunes

### `SA1006: Printf with dynamic first argument and no further arguments`

```go
msg := "hola mundo"
fmt.Printf(msg)       // staticcheck SA1006: posible format injection
fmt.Printf("%s", msg) // BIEN
fmt.Print(msg)        // O usar Print si no hay formato
```

### `S1039: Unnecessary use of fmt.Sprintf`

```go
// MAL — Sprintf innecesario
nombre := fmt.Sprintf("Juan")    // staticcheck S1039

// BIEN
nombre := "Juan"
```

## Gotchas / Errores comunes no obvios

- **`go mod tidy` genera un `go.sum` distinto en CI aunque no hubo cambios de dependencias**: la versión de Go difiere entre el entorno local y CI, ya que módulos directos y sus dependencias pueden resolverse con versiones de Go diferentes. Causa: Go embebe la versión mínima de Go en go.mod con `go 1.21`, y `go mod tidy` con una versión distinta puede agregar o quitar entradas del `go.sum`. Solución: fijar la versión de Go exacta en CI usando la misma que en `go.mod` (`go 1.21.x`).
- **`go build -race` reporta DATA RACE solo en CI, no localmente**: el detector de race conditions es no-determinista y depende del scheduling del OS. Causa: la ventana de concurrencia es demasiado pequeña para reproducirse con la carga de trabajo del laptop pero se manifiesta bajo la carga de CI. Solución: ejecutar el test con `-race -count=10` para aumentar la probabilidad de detección local; una vez detectado, el report de `go build -race` muestra exactamente las goroutines y líneas en conflicto.
- **Import cycle introducido por agregar un interface en el paquete A para que B no importe A directamente**: mover la interface al paquete "receptor" (A) no rompe el ciclo si B igual importa A para instanciar el tipo concreto. Causa: la interface en A solo mueve el punto de importación pero no elimina la dependencia si B sigue necesitando un tipo concreto de A. Solución: extraer la interface a un tercer paquete `pkgTypes` que ni A ni B importan entre sí, y que ambos referencian.
- **`staticcheck` SA4006 reporta variable asignada pero nunca usada en la línea del error handler**: el patrón `if err != nil { log.Error(err); return }` hace que la variable `err` de la siguiente asignación sea considerada "sobrescrita sin leer". Causa: el analyzer cuenta la asignación previa de `err` como usada solo si el valor se lee antes de la siguiente asignación — en cadenas de manejo de error con `err =` múltiple, la primera asignación puede quedar sin leer. Solución: en cadenas largas de errores, usar `:=` en cada asignación nueva para que el scope sea claro, no reutilizar la variable `err` con `=`.

## Checklist de diagnóstico

- [ ] `go build ./...` para ver todos los errores de una vez
- [ ] `go vet ./...` para detectar problemas de corrección
- [ ] `go mod tidy` si hay errores de módulos
- [ ] `go list -f '{{.IgnoredGoFiles}}' ./paquete/` para build constraints
- [ ] `go build -race ./...` si hay sospechas de concurrencia
- [ ] `staticcheck ./...` o `golangci-lint run` para análisis estático completo
- [ ] Para import cycles: `go build ./... 2>&1 | grep cycle`

## Referencias

- [Go Build Constraints](https://pkg.go.dev/cmd/go#hdr-Build_constraints)
- [Go Modules Reference](https://go.dev/ref/mod)
- [staticcheck](https://staticcheck.dev/docs/checks)
- [golangci-lint](https://golangci-lint.run/usage/linters/)

---
*Skill creado con swl:crear-skill el 2026-03-31. Versión 1.0.0.*
