nil pointer dereference em Go
O “runtime error: invalid memory address or nil pointer dereference” é um dos panics mais comuns em programas Go. Ele acontece quando você tenta acessar um campo, chamar um método ou desreferenciar um ponteiro que tem valor nil — ou seja, não aponta para nenhum endereço de memória válido.
Diferente de linguagens com exceções como Java (NullPointerException) ou C# (NullReferenceException), em Go esse erro causa um panic que, se não recuperado, derruba o programa inteiro.
A Mensagem de Erro
goroutine 1 [running]:
main.main()
/app/main.go:12 +0x1e
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47f68e]
Causas Comuns
1. Ponteiro Não Inicializado
Quando você declara um ponteiro sem inicializá-lo, ele é nil por padrão:
package main
import "fmt"
type Usuario struct {
Nome string
Email string
}
func main() {
var u *Usuario // u é nil
// PANIC: nil pointer dereference
fmt.Println(u.Nome)
}
2. Retorno nil Não Verificado
Funções que retornam ponteiros podem retornar nil em caso de erro:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("arquivo-inexistente.txt")
// Se ignorar err, file pode ser nil!
// PANIC ao tentar usar file
fmt.Println(file.Name())
_ = err
}
3. Map Não Inicializado com Structs
Maps retornam o zero value quando a chave não existe. Para ponteiros, isso é nil:
package main
import "fmt"
type Config struct {
Valor string
}
func main() {
configs := map[string]*Config{
"prod": {Valor: "producao"},
}
// "dev" não existe no map — retorna nil
dev := configs["dev"]
// PANIC: nil pointer dereference
fmt.Println(dev.Valor)
}
4. Interface com Valor nil
Uma interface pode conter um ponteiro nil, o que gera confusão:
package main
import "fmt"
type Logger interface {
Log(msg string)
}
type FileLogger struct {
Path string
}
func (f *FileLogger) Log(msg string) {
fmt.Printf("[%s] %s\n", f.Path, msg)
}
func getLogger() Logger {
var fl *FileLogger // nil
return fl // Interface não-nil contendo ponteiro nil!
}
func main() {
logger := getLogger()
// logger != nil (interface tem tipo, mas valor nil)
if logger != nil {
// PANIC: o valor dentro da interface é nil
logger.Log("teste")
}
}
5. Slice de Ponteiros
Quando você tem um slice de ponteiros e algum elemento é nil:
package main
import "fmt"
type Pedido struct {
ID int
Total float64
}
func main() {
pedidos := []*Pedido{
{ID: 1, Total: 99.90},
nil, // Elemento nil!
{ID: 3, Total: 45.00},
}
for _, p := range pedidos {
// PANIC quando p é nil
fmt.Printf("Pedido %d: R$ %.2f\n", p.ID, p.Total)
}
}
Como Resolver
Solução 1: Sempre Verificar nil
A forma mais direta — verifique antes de usar:
package main
import "fmt"
type Usuario struct {
Nome string
}
func buscarUsuario(id int) *Usuario {
if id == 1 {
return &Usuario{Nome: "Alice"}
}
return nil
}
func main() {
u := buscarUsuario(2)
// Verifica nil antes de acessar
if u != nil {
fmt.Println(u.Nome)
} else {
fmt.Println("Usuário não encontrado")
}
}
Solução 2: Tratar Erros Antes de Usar o Resultado
Siga o padrão idiomático de tratamento de erros em Go:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("config.json")
if err != nil {
fmt.Println("Erro:", err)
return // Sai antes de usar file
}
defer file.Close()
// Seguro: file não é nil aqui
fmt.Println("Arquivo:", file.Name())
}
Solução 3: Inicializar com Valor Padrão
Use construtores ou valores padrão para evitar ponteiros nil:
package main
import "fmt"
type Config struct {
Host string
Port int
}
// Construtor que nunca retorna nil
func NewConfig() *Config {
return &Config{
Host: "localhost",
Port: 8080,
}
}
func main() {
cfg := NewConfig() // Nunca nil
fmt.Printf("%s:%d\n", cfg.Host, cfg.Port)
}
Solução 4: Verificar Maps com ok
Use o idioma de dois valores para maps:
package main
import "fmt"
type Config struct {
Valor string
}
func main() {
configs := map[string]*Config{
"prod": {Valor: "producao"},
}
// Verifica se a chave existe
if dev, ok := configs["dev"]; ok {
fmt.Println(dev.Valor)
} else {
fmt.Println("Configuração 'dev' não encontrada")
}
}
Solução 5: Usar recover para Panics Inesperados
Em servidores HTTP e sistemas que não podem cair, use recover:
package main
import "fmt"
func processarSeguro(dados interface{}) (resultado string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recuperado: %v", r)
}
}()
// Código que pode causar panic
m := dados.(map[string]string)
return m["chave"], nil
}
func main() {
resultado, err := processarSeguro(nil)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println(resultado)
}
Técnicas de Debug
Stack Trace
Quando um nil pointer dereference acontece, Go imprime o stack trace completo. Leia de baixo para cima para encontrar a causa:
goroutine 1 [running]:
main.processarPedido(0x0) # <-- ponteiro 0x0 = nil!
/app/handlers.go:45 +0x1e
main.main()
/app/main.go:12 +0x3a
Delve Debugger
Use o Delve para depurar interativamente:
# Instalar
go install github.com/go-delve/delve/cmd/dlv@latest
# Executar com debugger
dlv debug .
Consulte nosso guia de profiling e performance para mais técnicas de debug.
Dicas para Evitar Este Erro
Prefira valores a ponteiros — se a struct é pequena, passe por valor ao invés de ponteiro. Sem ponteiro, sem nil.
Siga o padrão
if err != nil— nunca ignore o retorno de erro. Veja tratamento de erros em Go.Use construtores (New…) — funções como
NewConfig()garantem inicialização correta. Veja clean architecture em Go para padrões de projeto.Inicialize maps com
make—var m map[string]inté nil. Usem := make(map[string]int).Habilite
go vete linters — staticcheck e golangci-lint detectam possíveis nil dereferences.Escreva testes — testes unitários e testes de tabela com casos nil previnem regressões.
Vale notar que linguagens como Rust eliminam nil pointer dereferences em tempo de compilação usando Option<T>, e Kotlin aborda o problema com null safety nativo no sistema de tipos.
Erros Relacionados
- deadlock — all goroutines are asleep — outro panic comum em runtime
- race condition / data race — acessos concorrentes podem causar nil inesperado
- cannot use X as type Y — problemas de tipo com ponteiros vs valores
- Concorrência em Go — goroutines e compartilhamento seguro de dados