---
# === IDENTITY ===
id: software/debugging/go-nil-pointer/2026
canonical_question: "How do I debug nil pointer dereference in Go?"
aliases:
  - "Go nil pointer dereference"
  - "runtime error invalid memory address or nil pointer dereference"
  - "Go panic nil pointer"
  - "Go SIGSEGV nil dereference"
  - "golang nil pointer panic fix"
  - "Go interface nil check"
  - "nil map panic Go"
  - "uninitialized pointer Go"
entity_type: software_reference
domain: software > debugging > go_nil_pointer
region: global
jurisdiction: global
temporal_scope: 2012-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.95
version: 1.1
first_published: 2026-02-20

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "Go 1.25 (Aug 2025) fixed a Go 1.21–1.24 compiler bug that delayed nil pointer panics — nil dereferences now panic immediately as the spec requires"
  next_review: 2026-11-13
  change_sensitivity: low

# === AGENT HINTS ===
inputs_needed:
  - key: "panic_context"
    question: "Where does the nil pointer panic occur (stack trace line number)?"
    type: choice
    options: ["struct method call", "map access", "interface method", "function return value", "concurrent access", "unknown"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/debugging/go-nil-pointer/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-05-17)"

# === CONSTRAINTS ===
constraints:
  - "Go-specific only — nil pointer dereference in Go is not the same as null pointer exceptions in Java/C# or segfaults in C/C++; the runtime, tooling, and fix patterns differ"
  - "Always read the stack trace first — Go prints the exact file:line and pointer value (0x0) on panic; start debugging there, not by guessing"
  - "Interface nil vs typed nil is a distinct trap — an interface holding a nil concrete value is NOT == nil; standard nil checks will pass but method calls will panic [src6]"
  - "recover() in a deferred function catches the panic but does NOT fix the bug — program state is likely corrupt after nil dereference; use it only as a top-level safety net, never as a fix [src1]"
  - "Nil map writes panic but nil slice operations do not — this asymmetry is a frequent source of confusion; always make() maps before writing [src1]"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "The crash is a C or C++ segmentation fault (SIGSEGV from C code, not Go runtime)"
    use_instead: "software/debugging/c-cpp-segfault/2026"
  - condition: "The issue is a Go goroutine leak (gradual memory/goroutine growth, not a crash) — no panic, just resource exhaustion"
    use_instead: "software/debugging/go-goroutine-leak/2026"
  - condition: "The error is JavaScript 'Cannot read properties of undefined' or 'TypeError: x is undefined' — different language, different runtime"
    use_instead: "software/debugging/js-cannot-read-property-undefined/2026"
  - condition: "The error is a Java NullPointerException — different language, different patterns, different tooling (IntelliJ inspections, Optional<T>)"
    use_instead: "Generic Java NullPointerException guides (no knowledgelib card yet)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/debugging/go-goroutine-leak/2026"
      label: "Go Goroutine Leak Detection"
  often_confused_with:
    - id: "software/debugging/go-goroutine-leak/2026"
      label: "Go Goroutine Leaks (gradual resource exhaustion, not a crash)"
    - id: "software/debugging/c-cpp-segfault/2026"
      label: "C/C++ Segfaults (similar SIGSEGV symptom, different language and tools)"
    - id: "software/debugging/js-cannot-read-property-undefined/2026"
      label: "JavaScript undefined property errors (same concept, different language and runtime)"

# === SOURCES (9 authoritative sources) ===
sources:
  - id: src1
    title: "The Go Programming Language Specification — Run-time panics"
    author: The Go Authors
    url: https://go.dev/ref/spec#Run_time_panics
    type: official_docs
    published: 2024-02-06
    reliability: authoritative
  - id: src2
    title: "Help: Invalid memory address or nil pointer dereference"
    author: YourBasic Go
    url: https://yourbasic.org/golang/gotcha-nil-pointer-dereference/
    type: community_resource
    published: 2024-01-15
    reliability: high
  - id: src3
    title: "Go Panic: How to Fix 'invalid memory address or nil pointer dereference' Runtime Error"
    author: CodeGenes
    url: https://www.codegenes.net/blog/go-panic-runtime-error-invalid-memory-address-or-nil-pointer-dereference/
    type: technical_blog
    published: 2024-11-01
    reliability: high
  - id: src4
    title: "How to Avoid and Debug Nil Pointer Dereference Panics in Go"
    author: OneUptime
    url: https://oneuptime.com/blog/post/2026-01-23-go-nil-pointer-panics/view
    type: technical_blog
    published: 2026-01-23
    reliability: high
  - id: src5
    title: "Invalid Memory Address or Nil Pointer Dereference — Runbooks"
    author: Container Solutions
    url: https://containersolutions.github.io/runbooks/posts/golang/invalid-memory-address-or-nil-pointer-dereference/
    type: community_resource
    published: 2024-06-01
    reliability: high
  - id: src6
    title: "Much Ado About Nil Things: More Go Pitfalls"
    author: DoltHub
    url: https://www.dolthub.com/blog/2023-09-08-much-ado-about-nil-things/
    type: technical_blog
    published: 2023-09-08
    reliability: high
  - id: src7
    title: "Fixing Go error: nil pointer dereference"
    author: Sling Academy
    url: https://www.slingacademy.com/article/fixing-go-error-nil-pointer-dereference/
    type: technical_blog
    published: 2024-03-15
    reliability: high
  - id: src8
    title: "Go 1.25 Release Notes — nil pointer check correctness"
    author: The Go Authors
    url: https://go.dev/doc/go1.25
    type: official_docs
    published: 2025-08-12
    reliability: authoritative
  - id: src9
    title: "Interprocedural Analysis: Catch Nil Dereferences Before They Crash Your Code"
    author: JetBrains GoLand Team
    url: https://blog.jetbrains.com/go/2025/07/28/interprocedural-analysis-catch-nil-dereferences-before-they-crash-your-code/
    type: technical_blog
    published: 2025-07-28
    reliability: high
---

# How Do I Debug Nil Pointer Dereference in Go?

## TL;DR

- **Bottom line**: A nil pointer dereference panic (`runtime error: invalid memory address or nil pointer dereference`) occurs when you access a field, method, or index through a pointer that is `nil`. The fix is always one of: initialize the pointer, check for nil before access, or return an error instead of nil. Read the stack trace line number to find which variable is nil, then trace back to where it should have been assigned.
- **Key tool/command**: `dlv debug main.go` (Delve debugger) — set breakpoints, inspect pointer values, and step through code to find where a pointer becomes nil.
- **Watch out for**: An interface holding a nil concrete value is *not* `== nil`. This is the #1 subtle cause of nil panics that pass standard nil checks. [src6]
- **Works with**: All Go versions (Go 1.0+). Delve requires Go 1.21+. Static analyzers `go vet`, `staticcheck`, and `nilaway` work with Go 1.18+. **Use Go 1.25+ in production** — Go 1.21–1.24 had a compiler bug that could delay nil pointer panics past subsequent error checks, hiding real nil-deref bugs; Go 1.25 makes nil dereferences panic immediately as the spec requires. [src8]

## Constraints

- Never ignore the return value of functions that return `(*T, error)` — the pointer may be nil when `err != nil`. [src3]
- Never dereference a pointer without confirming it is non-nil, especially after type assertions, map lookups, or function returns. [src1]
- Never write to a nil map — this panics. A nil slice is safe to append to, but a nil map must be initialized with `make()` before writing. [src1]
- Never assume an interface is nil just because the underlying value is nil — check the concrete type separately or return a plain `nil` (not a typed nil pointer) from functions returning interfaces. [src6]
- Race conditions can set a pointer to nil between your nil check and dereference — use `sync.Mutex` or `sync.RWMutex` for shared pointer variables. [src3]

## Quick Reference

| # | Cause | Likelihood | Signature | Fix |
|---|---|---|---|---|
| 1 | Uninitialized pointer variable | ~30% of cases | `var p *MyStruct` then `p.Field` | Initialize: `p := &MyStruct{}` or `p := new(MyStruct)` [src2] |
| 2 | Function returns nil pointer (error not checked) | ~25% of cases | `result := getUser(id)` then `result.Name` | Check: `if result == nil { return err }` [src3, src5] |
| 3 | Nil map write | ~15% of cases | `var m map[string]int` then `m["key"] = 1` | Initialize: `m := make(map[string]int)` [src1] |
| 4 | Interface with nil concrete value | ~10% of cases | `var p *MyError = nil; var err error = p; err != nil` is `true` | Return plain `nil`, not typed nil: `return nil` [src6] |
| 5 | Uninitialized struct pointer field | ~8% of cases | `type S struct { Inner *T }; s := S{}; s.Inner.Field` | Use constructor: `NewS()` that initializes all fields [src7] |
| 6 | Nil method receiver | ~5% of cases | Calling method on nil pointer receiver | Add nil guard in method: `if p == nil { return zero }` [src4] |
| 7 | Nil function value | ~3% of cases | `var fn func(); fn()` | Check: `if fn != nil { fn() }` [src1] |
| 8 | Race condition sets pointer to nil | ~2% of cases | Intermittent panic under load | Add `sync.Mutex` around pointer access [src3] |
| 9 | Nil channel operations | ~2% of cases | `var ch chan int; ch <- 1` (blocks forever) or `close(ch)` (panics) | Initialize: `ch := make(chan int)` [src1] |

## Decision Tree

> Full script: [decision-tree.txt](scripts/decision-tree.txt) (27 lines)

```
START — panic: runtime error: invalid memory address or nil pointer dereference
├── Read stack trace → identify exact file:line
│   └── Which variable is being dereferenced on that line?
│
├── Is it a struct pointer variable?
# ... (see full script)
```

## Step-by-Step Guide

### 1. Read the stack trace to find the panic location

Every nil pointer panic prints a goroutine stack trace. The first line after `panic:` tells you the file and line number. [src3]

```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1092a9d]

goroutine 1 [running]:
main.processUser(0x0)
        /app/main.go:25 +0x1d      ← THIS LINE: file main.go, line 25
main.main()
        /app/main.go:12 +0x45
exit status 2
```

**Key details**:
- `addr=0x0` confirms nil dereference (address zero)
- `/app/main.go:25` is the exact line where the nil pointer was accessed
- `(0x0)` in the function args shows the pointer value was nil

**Verify**: Look at line 25 of main.go — identify which variable is a pointer on that line.

### 2. Identify which pointer is nil on that line

Examine the code at the panic line. Look for: field access (`p.Field`), method call (`p.Method()`), index operation (`s[i]`), or dereference (`*p`). [src3]

```go
// Line 25 in the stack trace — which pointer is nil?
func processUser(u *User) {
    fmt.Println(u.Name)  // ← u is nil here
}
```

**Verify**: Add a temporary log before the line: `fmt.Printf("u = %v\n", u)` — if it prints `<nil>`, you found it.

### 3. Trace back to where the pointer should have been assigned

Walk the call chain in the stack trace upward. Find where the nil pointer was created or returned. [src5]

```go
func main() {
    user := findUser(999)  // Returns nil when not found
    processUser(user)       // Passes nil to processUser
}

func findUser(id int) *User {
    if id > 100 {
        return nil  // ← ROOT CAUSE: returns nil without error
    }
    return &User{Name: "Alice"}
}
```

**Verify**: The root cause is `findUser` returning `nil`. The fix depends on the pattern (see Step 4).

### 4. Apply the appropriate fix pattern

Choose based on the cause identified in Step 3. [src3, src5, src7]

```go
// FIX A: Add nil check at call site
func main() {
    user := findUser(999)
    if user == nil {
        log.Println("user not found")
        return
    }
    processUser(user)
}

// FIX B: Return error instead of nil (preferred)
func findUser(id int) (*User, error) {
    if id > 100 {
        return nil, fmt.Errorf("user %d not found", id)
    }
    return &User{Name: "Alice"}, nil
}

func main() {
    user, err := findUser(999)
    if err != nil {
        log.Fatal(err)
    }
    processUser(user)
}

// FIX C: Return zero value instead of nil
func findUser(id int) *User {
    if id > 100 {
        return &User{} // Safe zero value instead of nil
    }
    return &User{Name: "Alice"}
}
```

**Verify**: `go run main.go` — no panic. `go vet ./...` — no warnings.

### 5. Run static analysis to find other nil dereference risks

Catch similar issues across the entire codebase before they panic in production. [src4]

```bash
# Built-in vet (catches some nil issues)
go vet ./...

# Staticcheck (comprehensive linter)
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...

# Nilaway (Uber's nil safety analyzer — catches cross-function nil flows)
go install go.uber.org/nilaway/cmd/nilaway@latest
nilaway ./...

# Race detector (catches concurrent nil pointer issues)
go test -race ./...
```

**Verify**: Fix all reported issues. Run `go build ./...` to confirm no compilation errors.

### 6. Use Delve for complex nil pointer debugging

When the nil source is not obvious from code inspection, use Delve to step through execution. [src4]

```bash
# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Start debugging
dlv debug ./cmd/myapp

# Set breakpoint at the panic line (from stack trace)
(dlv) break main.go:25

# Run until breakpoint
(dlv) continue

# Inspect the nil pointer
(dlv) print u
# Output: *main.User nil

# Step back — who set u to nil?
(dlv) stack
# Shows full call stack with arguments

# Set conditional breakpoint
(dlv) break main.go:12 cond user == nil

# Restart and run with condition
(dlv) restart
(dlv) continue
```

**Verify**: Delve stops at the breakpoint showing the nil variable. Inspect the call chain to find root cause.

## Code Examples

### Go: nil-safe method receiver pattern

> Full script: [go-nil-safe-method-receiver-pattern.go](scripts/go-nil-safe-method-receiver-pattern.go) (25 lines)

```go
// Input:  A struct with methods that may be called on nil receivers
// Output: Methods that return zero values instead of panicking
type Config struct {
    Host string
    Port int
# ... (see full script)
```

### Go: generic safe dereference helpers (Go 1.18+)

> Full script: [go-generic-safe-dereference-helpers-go-1-18.go](scripts/go-generic-safe-dereference-helpers-go-1-18.go) (26 lines)

```go
// Input:  Any pointer that might be nil
// Output: The value, or a safe default
// Deref returns the pointed-to value, or zero value if nil
func Deref[T any](p *T) T {
    if p == nil {
# ... (see full script)
```

### Go: constructor pattern that prevents nil fields

```go
// Input:  Required dependencies for a service
// Output: Fully initialized struct or error — never a nil field

type UserService struct {
    db     *sql.DB
    cache  *redis.Client
    logger *slog.Logger
}

// NewUserService validates all dependencies are non-nil
func NewUserService(db *sql.DB, cache *redis.Client, logger *slog.Logger) (*UserService, error) {
    if db == nil {
        return nil, fmt.Errorf("NewUserService: db must not be nil")
    }
    if cache == nil {
        return nil, fmt.Errorf("NewUserService: cache must not be nil")
    }
    if logger == nil {
        logger = slog.Default() // Provide safe default
    }
    return &UserService{db: db, cache: cache, logger: logger}, nil
}
```

## Anti-Patterns

### Wrong: Ignoring error return and dereferencing pointer

```go
// BAD — if GetUser returns nil on error, this panics [src3]
user, _ := svc.GetUser(ctx, id)
fmt.Println(user.Name) // panic if user is nil
```

### Correct: Always check error before using pointer

```go
// GOOD — handle error first [src3]
user, err := svc.GetUser(ctx, id)
if err != nil {
    return fmt.Errorf("get user %d: %w", id, err)
}
fmt.Println(user.Name) // safe — err was nil, so user is not nil
```

### Wrong: Returning typed nil pointer in interface return

```go
// BAD — caller's `err != nil` check will be TRUE even on success [src6]
func validate(s string) error {
    var err *ValidationError // typed nil
    if s == "" {
        err = &ValidationError{Msg: "empty"}
    }
    return err // returns non-nil interface with nil *ValidationError!
}
// validate("hello") returns non-nil error!
```

### Correct: Return plain nil for interface types

```go
// GOOD — return untyped nil explicitly [src6]
func validate(s string) error {
    if s == "" {
        return &ValidationError{Msg: "empty"}
    }
    return nil // plain nil — caller's err == nil check works correctly
}
```

### Wrong: Writing to uninitialized map

```go
// BAD — nil map write panics [src1]
var counts map[string]int
counts["hello"] = 1 // panic: assignment to entry in nil map
```

### Correct: Initialize map before use

```go
// GOOD — always use make() for maps [src1]
counts := make(map[string]int)
counts["hello"] = 1 // safe
```

### Wrong: No nil guard on method receiver

```go
// BAD — calling method on potentially nil receiver [src4]
type Node struct {
    Value int
    Next  *Node
}

func (n *Node) String() string {
    return fmt.Sprintf("%d -> %s", n.Value, n.Next.String()) // panics when Next is nil
}
```

### Correct: Nil guard on receiver

```go
// GOOD — handle nil receiver explicitly [src4]
func (n *Node) String() string {
    if n == nil {
        return "<nil>"
    }
    return fmt.Sprintf("%d -> %s", n.Value, n.Next.String()) // safe recursion
}
```

## Common Pitfalls

- **Interface nil confusion**: An interface that holds a nil pointer is NOT `== nil`. The interface has type information even though its value is nil. Always return plain `nil` (not a typed nil variable) from functions that return interfaces. Fix: `return nil` instead of `return (*MyType)(nil)`. [src6]
- **Unchecked error returns**: Functions returning `(*T, error)` can return `nil` for the pointer when the error is non-nil. Dereferencing without checking the error is the #1 cause of nil panics in production code. Fix: always `if err != nil { return err }` before using the pointer. [src3]
- **Nil map vs nil slice asymmetry**: A nil slice is safe for `len()`, `cap()`, `range`, and `append()`. A nil map is safe for `len()` and read access (returns zero value) but panics on write. Fix: always `make()` maps before writing. [src1]
- **Struct zero value has nil pointer fields**: `MyStruct{}` initializes value fields to zero but pointer fields to nil. Accessing `s.PointerField.SubField` panics. Fix: use constructor functions that initialize all pointer fields. [src7]
- **Race conditions with shared pointers**: A pointer checked for nil in one goroutine can be set to nil by another goroutine between the check and dereference. Fix: protect shared pointers with `sync.Mutex` and run `go test -race`. [src3]
- **Type assertion on nil interface panics**: `x.(ConcreteType)` panics if `x` is nil. Use the two-value form: `v, ok := x.(ConcreteType)`. Fix: always use the comma-ok idiom for type assertions. [src1]

## Diagnostic Commands

```bash
# Run with race detector (catches concurrent nil pointer issues)
go run -race main.go
go test -race ./...

# Static analysis — built-in
go vet ./...

# Static analysis — comprehensive linter
staticcheck ./...

# Nil-specific analyzer (Uber's nilaway)
nilaway ./...

# Install and run Delve debugger
go install github.com/go-delve/delve/cmd/dlv@latest
dlv debug ./cmd/myapp
# At the dlv prompt:
#   break main.go:25
#   continue
#   print variableName
#   stack

# Build with full debug info (for better stack traces)
go build -gcflags="all=-N -l" -o myapp ./cmd/myapp

# Print stack trace of running process (if panic handler installed)
kill -SIGQUIT <pid>  # prints all goroutine stacks (Linux/macOS)

# Run tests with verbose output for nil-related failures
go test -v -count=1 ./...
```

## Version History & Compatibility

| Feature | Available Since | Notes |
|---|---|---|
| Nil pointer panic behavior | Go 1.0 | Fundamental language spec — stable since inception [src1] |
| `go vet` nil checks | Go 1.1 | Basic nil dereference detection [src1] |
| Race detector (`-race` flag) | Go 1.1 | Detects concurrent nil pointer races [src1] |
| Delve debugger | Go 1.5+ | Full nil pointer inspection; requires Go 1.21+ for latest [src4] |
| Generic helpers (`Deref[T]`) | Go 1.18 | Requires generics support [src4] |
| `staticcheck` nil analysis | Go 1.18+ | SA5011 checks for nil pointer dereference [src4] |
| `nilaway` (Uber) | Go 1.20+ | Cross-function nil flow analysis [src4] |
| GoLand interprocedural nil analysis | GoLand 2025.2 (Jul 2025) | Tracks nil flow across files/packages with a Data Flow Analysis tool window; <5% build overhead [src9] |
| Nil pointer panic correctness fix | Go 1.25 (Aug 2025) | Reverses a Go 1.21–1.24 compiler bug that delayed nil checks past subsequent statements; nil deref now panics immediately as spec requires [src8] |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Panic message says "invalid memory address or nil pointer dereference" | Panic says "index out of range" | Bounds checking / len() guard |
| Stack trace points to pointer field access | Error is "assignment to entry in nil map" (subset — map-specific) | `make(map[K]V)` initialization |
| Intermittent crash under concurrent load | Deterministic crash on startup | Check initialization order |
| Interface `!= nil` but method panics | Interface is truly nil | Standard nil check suffices |
| Need to prevent nil panics at compile time | Need to prevent at runtime only | Use nilaway/staticcheck instead of defer/recover |

## Important Caveats

- **Recover does not fix nil dereference**: While `defer`/`recover` can catch nil pointer panics, it masks the bug rather than fixing it. The program state is likely corrupt after a nil dereference. Only use recover at top-level request handlers as a safety net, never as a fix. [src1]
- **Nil pointer panics are deterministic (unless concurrent)**: If the panic is intermittent, suspect a race condition. Run `go test -race` to confirm. Non-concurrent nil panics always reproduce with the same inputs. [src3]
- **Different nil types are not equal in interfaces**: `(*int)(nil)` and `(*string)(nil)` assigned to `interface{}` produce different non-nil values that are not equal to each other or to `nil`. [src6]
- **The `new()` function returns a pointer to zeroed memory, not nil**: `p := new(MyStruct)` gives you `&MyStruct{}`, not nil. But pointer fields within that struct are still nil. [src2]
- **Go 1.21–1.24 could silently skip nil panics**: A compiler bug introduced in Go 1.21 sometimes reordered nil checks so a dereference like `f.Name()` ran *before* the matching `if err != nil { return }` — masking the bug instead of panicking. Go 1.25 (Aug 2025) restores the spec-mandated immediate panic. If you maintain a service still on Go 1.21–1.24, assume any "impossible" panic-free code path through `(*T, error)` returns is a hidden nil bug and audit it. [src8]

## Related Units

- [Go Goroutine Leak Detection](/software/debugging/go-goroutine-leak/2026)
- [C/C++ Segfault Debugging](/software/debugging/c-cpp-segfault/2026)
- [JavaScript "Cannot read properties of undefined"](/software/debugging/js-cannot-read-property-undefined/2026)
