Go — Nil Pointer Dereference Panic
Trace a nil pointer panic from the stack trace, find the unchecked nil value, and fix it with explicit error handling and regression tests.
Symptoms
- panic: runtime error: invalid memory address or nil pointer dereference
- The service exits or the HTTP handler returns 500 with a stack trace
- The panic appears only when a dependency returns an error or a lookup misses
Possible causes
- A pointer field or method is used before checking whether it is nil
- A function returns nil with an error that was ignored
- A map lookup or type assertion returns a zero value that is used as a valid object
- An interface holds a nil concrete value and is treated as non-nil
Diagnosis steps
Read the panic stack trace from the innermost frame
go test ./... 2>&1 | grep -B5 -A10 'nil pointer'Interpretation
The topmost application frame identifies the exact dereference. Note the file and line, not just the package.
Reproduce with the race detector and verbose tests
go test -race -run TestName -v ./...Interpretation
The race detector does not catch all nil panics, but it reveals concurrent access that may produce nil values. Add a focused test for the failing input.
Inspect error returns and map lookups around the panic site
grep -n 'err\|, ok :=\|return nil' path/to/file.goInterpretation
An ignored error or a missing ok check often explains why nil reached the dereference.
Root cause
Go panics when a nil pointer is dereferenced. The value became nil because an error was ignored, a lookup missed, a constructor returned nil, or an interface contains a nil concrete value while comparing non-nil.
Fix
Check errors before using results, use the two-value form for maps and type assertions, return early on nil, and initialize pointers before use. If nil is valid, guard the dereference and define the fallback behavior explicitly.
Risk note
recover() can turn a panic into a 500 and hide the root cause. Use it only at process or request boundaries with logging and metrics, never as the primary nil check.
Verify the fix
Add a regression test for the input that produced nil and run go test -race. Confirm the handler returns a defined error instead of panicking, and check logs for the original cause.
The stack trace is the map
A nil pointer panic tells you the exact dereference site. Start at the top application frame and work outward. The bug is usually an ignored error or a missing lookup check just before that line.
user, err := store.FindUser(ctx, id)
if err != nil {
return fmt.Errorf("find user: %w", err)
}
// If FindUser can return (nil, nil), this line panics.
return user.Email, nil
Fix patterns
user, err := store.FindUser(ctx, id)
if err != nil {
return "", fmt.Errorf("find user %s: %w", id, err)
}
if user == nil {
return "", ErrUserNotFound
}
return user.Email, nil
For maps, use value, ok := m[key]. For interfaces, remember that an interface holding a typed nil is not equal to nil; check the concrete value or avoid returning typed nil pointers as interfaces.
Verify
- The failing input now returns a defined error.
go test -racepasses.- A regression test reproduces the original path.
- Logs include the operation and identifier, not only “nil pointer”.
Prevention
Return errors with context, avoid constructors that return nil without an error, and review nil checks on every path where a dependency can fail. Run go vet and static analysis in CI; they catch some, but not all, nil flows.