Fix Go "runtime error: invalid memory address or nil pointer dereference"

beginner๐Ÿ”ท Go2026-03-24| Go 1.13+ on Linux, macOS, Windows

Error Message

runtime error: invalid memory address or nil pointer dereference
#go#nil#pointer#panic

The Error

Your Go program crashes mid-run, dumps a stack trace, and exits with status 2:

goroutine 1 [running]:
main.main()
        /home/user/app/main.go:15 +0x1c
exit status 2
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47d8a6]

The culprit: your code tried to read or write through a pointer sitting at address 0x0 โ€” Go's way of saying "this was never assigned anything real." That address is always invalid, so the OS kills the process on the spot.

Root Cause

Go doesn't auto-initialize pointer types, interfaces, maps, slices, channels, or function values. Their zero value is nil. Touch any of them before assigning a real value, and you'll get this panic.

Five scenarios that cause it constantly in real codebases:

  • Calling a method on a nil struct pointer (var u *User; u.Name())
  • Discarding the error from a function that can return nil on failure
  • Accessing a map or slice declared with var but never initialized
  • Storing an interface that wraps a nil concrete pointer
  • Using a failed type assertion result without checking ok

Fix 1 โ€” Check for nil before use

Guard every pointer or interface before you touch it. One extra if is all it takes.

// Bad โ€” panics if resp is nil
func processResponse(resp *http.Response) string {
    return resp.Status
}

// Good โ€” explicit nil check
func processResponse(resp *http.Response) string {
    if resp == nil {
        return "no response"
    }
    return resp.Status
}

Fix 2 โ€” Always check errors from functions that return pointers

This is the most common source of nil panics in Go. Functions like os.Open, database queries, and JSON unmarshal all return (value, error). When the call fails, value is nil โ€” and if you silence the error with _, you're one line away from a crash.

// Bad โ€” ignores the error, then panics on nil
f, _ := os.Open("config.json")
data, _ := io.ReadAll(f)   // f is nil if Open failed

// Good โ€” check the error first
f, err := os.Open("config.json")
if err != nil {
    log.Fatal(err)
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
    log.Fatal(err)
}

Fix 3 โ€” Initialize structs and maps before use

Declaring a struct with var leaves all its pointer fields as nil. You have to explicitly initialize nested structs before writing to them.

// Bad โ€” Config.DB is never initialized
type Config struct {
    DB *DatabaseConfig
}

var cfg Config
cfg.DB.Host = "localhost"  // panic: DB is nil

// Good โ€” initialize the nested struct
cfg := Config{
    DB: &DatabaseConfig{},
}
cfg.DB.Host = "localhost"

// Or with new()
cfg.DB = new(DatabaseConfig)
cfg.DB.Host = "localhost"

Maps hit the same wall:

// Bad
var cache map[string]int
cache["key"] = 1  // panic: assignment to entry in nil map

// Good
cache := make(map[string]int)
cache["key"] = 1

Fix 4 โ€” Use the comma-ok pattern for type assertions

Skip the ok check on a type assertion and it panics the moment the type doesn't match. Two extra characters save you from the crash.

// Bad
var i interface{} = "hello"
n := i.(int)   // panic: interface conversion

// Good
n, ok := i.(int)
if !ok {
    fmt.Println("not an int")
    return
}

Fix 5 โ€” Reading the stack trace to find the exact line

Don't guess โ€” the panic output tells you precisely where it happened. Set GOTRACEBACK=all before you start digging:

GOTRACEBACK=all go run main.go

Scan the trace for the first frame inside your own package, not Go runtime internals. That's your nil dereference. Here's a concrete example:

panic: runtime error: invalid memory address or nil pointer dereference

goroutine 1 [running]:
main.processUser(...)
        /home/user/app/main.go:23   โ† your code, line 23
main.main()
        /home/user/app/main.go:10 +0x68

Jump to line 23 in main.go and look for a pointer or interface used without a nil guard.

Fix 6 โ€” Use recover only as a last resort

Building a server or middleware that must survive unexpected panics? Wrap risky calls with defer + recover. Just be clear about what this is: a crash net, not a fix. You still need to root-cause the nil and plug it properly.

func safeCall(fn func()) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    fn()
    return nil
}

Verification

Run the program again and confirm the panic is gone:

go run main.go

Better yet, pin the fix with a unit test so it can't quietly regress:

func TestProcessResponseNil(t *testing.T) {
    result := processResponse(nil)
    if result != "no response" {
        t.Errorf("expected 'no response', got %q", result)
    }
}

Run go test ./... โ€” green output with no panics means the fix holds.

Prevention

  • Run with the race detector during development: go run -race main.go โ€” catches concurrent nil writes that are nearly impossible to reproduce otherwise.
  • go vet ./... before every commit; it flags some nil dereference patterns statically at zero cost.
  • staticcheck and golangci-lint catch nil pointer risks that go vet misses โ€” worth wiring into CI.
  • Return concrete errors instead of nil pointers when a function fails. It makes nil impossible to silently ignore.
  • For heap-only structs, write a constructor: func NewUser() *User { return &User{scores: make([]int, 0)} } โ€” callers always get an initialized value.

Related Error Notes