Introduction
In Go, dereferencing a nil pointer causes a runtime panic that crashes the program. This commonly happens when accessing optional fields in structs returned from JSON unmarshaling, database queries, or API responses where nested objects may be absent.
Unlike languages with null-safe operators, Go requires explicit nil checks before every pointer dereference.
Symptoms
- Application crashes with "panic: runtime error: invalid memory address or nil pointer dereference"
- Stack trace points to a line accessing a struct field or method
- Panic occurs only for certain API responses or database records (not all)
Common Causes
- JSON response omits an optional nested object, leaving the pointer as nil
- Database query returns NULL for a column mapped to a pointer field
- Code assumes a function always returns a non-nil value
Step-by-Step Fix
- 1.Add nil checks before pointer dereference: Explicitly check for nil before accessing pointer fields.
- 2.```go
- 3.type APIResponse struct {
- 4.Data *DataObject
json:"data" - 5.}
type DataObject struct {
User *User json:"user"
Address *Address json:"address"
}
// BAD: panics if data.User or data.User.Address is nil name := resp.Data.User.Name
// GOOD: check each level if resp.Data != nil && resp.Data.User != nil { name := resp.Data.User.Name if resp.Data.User.Address != nil { street := resp.Data.User.Address.Street } } ```
- 1.Use helper methods for safe access: Encapsulate nil-safe access in methods.
- 2.```go
- 3.func (d *DataObject) GetUserName() string {
- 4.if d == nil || d.User == nil {
- 5.return ""
- 6.}
- 7.return d.User.Name
- 8.}
func (d *DataObject) GetStreet() string { if d == nil || d.User == nil || d.User.Address == nil { return "" } return d.User.Address.Street }
// Usage - no panic possible: name := data.GetUserName() street := data.GetStreet() ```
- 1.Use sql.NullString for nullable database fields: Handle database NULL values safely.
- 2.```go
- 3.import "database/sql"
type User struct { ID int Name sql.NullString // Can be NULL in database Email sql.NullString }
// Safe access: if user.Name.Valid { fmt.Println(user.Name.String) } else { fmt.Println("Name not set") } ```
- 1.Enable static analysis with nilaway: Use Uber's nilaway to detect nil dereference at compile time.
- 2.```bash
- 3.# Install nilaway:
- 4.go install go.uber.org/nilaway/cmd/nilaway@latest
# Run on your code: nilaway ./...
# Output shows potential nil dereferences: # nilaway: potential nil panic in file.go:42:15 # dereference of nil-able value 'resp.Data.User' ```
Prevention
- Always check pointers for nil before dereferencing, especially from external sources
- Use helper methods to encapsulate nil-safe access patterns
- Run nilaway or go-critic in CI to catch potential nil dereferences
- Use sql.Null* types for nullable database columns instead of raw pointers