Invalid Memory Address or Nil Pointer Dereference in Go
Fix the Go panic invalid memory address or nil pointer dereference: the common causes, how to read the trace, nil-safe methods and tests that prevent it.

This is the panic that Go programmers meet most often, and the one that has the most reasons to occur. The message is always the same: panic: runtime error: invalid memory address or nil pointer dereference, followed by a line starting [signal SIGSEGV: segmentation violation ...] and a goroutine trace. It means exactly one thing: the program followed a pointer that was nil. Everything else, which pointer, why it was nil, is in the code, and the trace points at the place.
Tested with Go 1.25.6, macOS on Apple Silicon (arm64)
This page shows how to read the trace, then the causes in order of frequency, each with a reproduction. It belongs to the error handling guide and has relatives in index out of range and the nil map write. Everything ran on Go 1.25.6.
Reading the trace
A trace has this shape (the paths and addresses here are illustrative), and the parts that matter are few:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x...]
goroutine 1 [running]:
main.(*Server).Handle(...)
/app/server.go:42
main.main()
/app/main.go:17 +0x1c
exit status 2Read it from the top. The first line says what happened. The addr=0x8 in the signal line is the address the program tried to read, and a small number (0x0, 0x8, 0x10) means a field at that offset of a nil struct pointer, which confirms the cause. Then find the first frame in your own code, here server.go:42, and open that line. The expression there contains a pointer that was nil: a field access s.conn.Close() where s.conn is nil, or a method that uses its receiver. The frames below it are the callers and show the path that led there. Frames from runtime or the standard library sit above yours if the panic happened inside them, and then the first of your frames is the call you made with the bad value.
You can reproduce the message and recover it, to see it without a crash:
package main
import "fmt"
type Server struct {
Name string
Conn *Conn
}
type Conn struct{ ID int }
func main() {
defer func() { fmt.Println("recovered:", recover()) }()
s := &Server{Name: "api"} // Conn was never set
fmt.Println(s.Name)
fmt.Println(s.Conn.ID)
}api
recovered: runtime error: invalid memory address or nil pointer dereferences is fine and s.Conn is nil, so reading .ID through it panics. The first Println works, which is a typical sign that the failing expression is one step deeper than the object you are looking at.
Cause 1: using a result after ignoring the error
By far the most common source is a function that returns (*T, error), called with the error dropped. On failure it returns nil and an error, and the code uses the nil:
package main
import (
"fmt"
"os"
)
func main() {
defer func() { fmt.Println("recovered:", recover()) }()
f, _ := os.Open("/does/not/exist")
fmt.Println(f.Name())
defer f.Close()
buf := make([]byte, 4)
f.Read(buf)
}recovered: runtime error: invalid memory address or nil pointer dereferenceos.Open returned a nil *os.File and an error that was thrown away with _. Interestingly, f.Name() on a nil *os.File panics with this message, because the method reads a field of the receiver. The fix is the same one it has been since the first page: check the error before using the value, and return when it is not nil (error handling in Go). The unchecked-error linter finds these before they run (errcheck).
Cause 2: a struct field that nobody set
A struct with a pointer, map, slice-of-pointers or interface field starts with the zero value, which is nil. Code that assumes the field is initialised panics at the first use:
package main
import "fmt"
type Cache struct{ hits int }
func (c *Cache) Hit() { c.hits++ }
type Service struct {
cache *Cache
}
func NewService() *Service {
return &Service{} // forgot cache
}
func (s *Service) Get() string {
s.cache.Hit()
return "value"
}
func main() {
defer func() { fmt.Println("recovered:", recover()) }()
fmt.Println(NewService().Get())
}recovered: runtime error: invalid memory address or nil pointer dereferenceThe method Hit has a pointer receiver and was called on a nil *Cache. Calling a method on a nil pointer is legal, and the panic happens on c.hits++, the first access to a field. The fix is to initialise every dependency in the constructor, and to make construction the only way to get a Service (unexported fields and a New function). If a dependency is optional, give it a no-op default instead of nil (constructors and functional options show how).
Cause 3: a nil interface or function value
Calling a method on a nil interface panics with the same message, because there is no concrete value to dispatch to. So does calling a nil function variable:
package main
import "fmt"
type Notifier interface{ Notify(string) }
type Handler struct {
Notifier Notifier
OnDone func()
}
func try(name string, f func()) {
defer func() { fmt.Printf("%-14s %v\n", name, recover()) }()
f()
}
func main() {
h := Handler{}
try("nil interface", func() { h.Notifier.Notify("hi") })
try("nil func", func() { h.OnDone() })
}nil interface runtime error: invalid memory address or nil pointer dereference
nil func runtime error: invalid memory address or nil pointer dereferenceBoth panic with invalid memory address or nil pointer dereference. Guard optional callbacks with an if h.OnDone != nil check, or fill them with a no-op at construction. For interfaces there is a second trap: an interface holding a typed nil pointer is not nil, so if n != nil passes and the method then panics inside, or worse, runs with a nil receiver. The nil interface that is not nil explains why.
Cause 4: nil from a map lookup or a type assertion
Map values of pointer type come back nil when the key is absent, and code that goes straight to a field panics:
package main
import "fmt"
type User struct{ Name string }
func main() {
defer func() { fmt.Println("recovered:", recover()) }()
users := map[string]*User{"ada": {Name: "Ada"}}
fmt.Println(users["ada"].Name)
fmt.Println(users["linus"].Name)
}Ada
recovered: runtime error: invalid memory address or nil pointer dereferenceUse the two-value form, u, ok := users["linus"], and check ok (checking for a key). The same applies to a lookup in a slice of pointers and to the result of x.(*T) when written without the ok form.
Cause 5: a pointer that was valid and no longer is
Race conditions and lifecycle mistakes produce nil pointers that were not nil a moment ago: a field cleared by Close while another goroutine reads it, a struct reused after reset, a callback that fires after teardown. These show up intermittently. The race detector finds the unsynchronised ones (the race detector), and the cure is to make ownership explicit, with a mutex around the field or a channel that serialises access.
Making methods safe on nil
Sometimes a nil receiver is a legitimate state, and the type can say so. A common idiom in the standard library is a method that treats nil as empty:
package main
import "fmt"
type Tree struct {
Left, Right *Tree
Value int
}
// Sum of a nil tree is zero, so the recursion needs no nil checks of its own.
func (t *Tree) Sum() int {
if t == nil {
return 0
}
return t.Value + t.Left.Sum() + t.Right.Sum()
}
func main() {
t := &Tree{Value: 1, Left: &Tree{Value: 2}, Right: &Tree{Value: 3, Left: &Tree{Value: 4}}}
var empty *Tree
fmt.Println(t.Sum(), empty.Sum())
}10 0A nil *Tree is an empty tree, and every method that handles it makes callers simpler. Use this where "nothing" is a natural value of the type, as with trees, lists and builders, and not to hide missing dependencies: a nil Service that silently returns defaults is a bug that stopped panicking.
Preventing it
- Check errors, always. Most nil pointers are an unchecked error in disguise.
- Initialise in constructors, and keep the struct's fields unexported so nobody builds a half-set value.
- Return values, not pointers, when nil is not meaningful: a
time.Durationor a small struct cannot be nil. - Use the comma-ok forms for maps and assertions.
- Write the test with the nil. A table row with a nil dependency or a failing constructor reaches the branch that production reaches at 3 a.m. (testing error paths).
- Run with
-racein CI to catch the pointers that vanish under concurrency.
For the exact semantics of nil in Go, the specification's section on the zero value and its rules for selectors say when a nil pointer dereference panics. The official runtime error text is documented in the runtime package.


