Fix

"invalid memory address or nil pointer dereference" in Go

Fix the 'invalid memory address or nil pointer dereference' panic in Go by ensuring pointers are initialized before use.

The nil pointer panic

You are running your Go server. A request arrives. The handler parses the query parameters, looks up a user in the database, and tries to print the user's email. The server crashes. The terminal fills with a stack trace. At the top, in bold, is the message: panic: invalid memory address or nil pointer dereference.

Your application is dead. The user sees a 500 error. The line number points to code that looks perfectly valid. You defined the struct. You called the function. You accessed the field. Everything type-checks. The problem is not the type. The problem is the value. You tried to read a field from a pointer that points to nothing.

Pointers and the empty address

A pointer is a variable that holds a memory address. It is a label with a location written on it. When you use the * operator or access a field like ptr.Field, you are dereferencing the pointer. You are following the address to find the actual data.

nil is a special value for pointers. It means the pointer holds no address. It is an empty label. Trying to dereference a nil pointer is like taking an empty envelope to the post office and asking the clerk to deliver the letter inside. There is no letter. There is no destination. The system has no idea where to look, so it stops execution and panics.

Go makes pointers explicit. If you see a * in a type, you know that value can be nil. If you see a plain type like string or int, you know it always holds a value. This explicitness is a feature. It warns you that a value might be missing before you try to use it.

Convention aside: receiver names in Go are usually one or two letters matching the type. Write (u *User) GetName(), not (this *User) or (self *User). Short names reduce noise and make the code easier to scan.

Minimal example

Here is the simplest way to trigger the panic and how to fix it.

package main

import "fmt"

type Config struct {
    Port int
    Host string
}

func main() {
    // Declare a pointer variable.
    // It starts as nil because no memory has been allocated.
    var cfg *Config

    // This line panics.
    // cfg is nil, so there is no memory to write the port to.
    // cfg.Port = 8080 // panic: invalid memory address or nil pointer dereference

    // Fix: initialize the struct before using the pointer.
    // The & operator takes the address of the new struct.
    cfg = &Config{}
    cfg.Port = 8080
    cfg.Host = "localhost"

    fmt.Println("Config loaded:", cfg)
}

The compiler does not catch this error. Go's compiler checks types, not values. It sees that cfg is a *Config. It sees that Port is an int. The types match. The compiler assumes you know what you are doing. The check happens at runtime. When the CPU tries to calculate the memory address for cfg.Port, it adds the offset of Port to the address stored in cfg. Since cfg is zero, the resulting address is invalid. The operating system detects the bad access and sends a signal. Go catches the signal and turns it into a panic.

The compiler trusts you. Don't betray that trust.

Realistic scenario: the forgotten error check

In real code, nil pointer panics rarely come from uninitialized variables. They come from functions that return pointers and errors. You call the function, you get a pointer, and you forget to check the error. The pointer is nil, and you use it anyway.

package main

import (
    "errors"
    "fmt"
)

type User struct {
    ID   int
    Name string
}

// GetUser retrieves a user from the database.
// It returns nil and an error if the user does not exist.
func GetUser(id int) (*User, error) {
    if id == 0 {
        return nil, errors.New("user not found")
    }
    return &User{ID: id, Name: "Alice"}, nil
}

func main() {
    // Call the function with an invalid ID.
    user, err := GetUser(0)

    // Mistake: checking the error but not returning.
    // If you log the error and continue, user is still nil.
    if err != nil {
        fmt.Println("Error fetching user:", err)
        // Missing return here leads to panic below.
    }

    // This line panics because user is nil.
    // fmt.Println("Welcome back,", user.Name) // panic: invalid memory address or nil pointer dereference

    // Fix: return or handle the error immediately.
    if err != nil {
        fmt.Println("Cannot proceed without user")
        return
    }

    fmt.Println("Welcome back,", user.Name)
}

Convention aside: if err != nil { return err } is verbose by design. The Go community accepts the boilerplate because it makes the unhappy path visible. You cannot accidentally ignore an error if you have to write the check. This pattern prevents nil pointer panics by forcing you to stop using the result when something goes wrong.

Another common trap is the map lookup. Maps in Go return the zero value for the value type if the key is missing. If the map holds pointers, the zero value is nil.

package main

import "fmt"

func main() {
    // Map of user IDs to user pointers.
    users := map[int]*User{
        1: {ID: 1, Name: "Alice"},
    }

    // Lookup a missing key.
    // users[99] returns the zero value for *User, which is nil.
    user := users[99]

    // Accessing a field on the nil pointer panics.
    // fmt.Println(user.Name) // panic: invalid memory address or nil pointer dereference

    // Fix: check for nil after the lookup.
    if user != nil {
        fmt.Println("Found:", user.Name)
    } else {
        fmt.Println("User not found in map")
    }
}

Maps return zero values. Pointers in maps return nil. Check the result.

Pitfalls and hidden traps

Nested pointers

If a struct contains pointer fields, you can have a valid struct with nil fields. Accessing a nested pointer without checking causes a panic.

type Address struct {
    City string
}

type Person struct {
    Name    string
    Address *Address
}

func main() {
    p := &Person{Name: "Bob"}
    // p is valid, but p.Address is nil.
    // This panics.
    // fmt.Println(p.Address.City) // panic: invalid memory address or nil pointer dereference
}

You must check each level of indirection. If p is not nil, p.Address might still be nil.

Method calls on nil receivers

Go allows you to call a method on a nil pointer, provided the method does not dereference the receiver. This is a deliberate feature. It lets you write methods that handle the nil case gracefully.

package main

import "fmt"

type Server struct {
    Port int
}

// String returns a description of the server.
// It handles the nil receiver safely.
func (s *Server) String() string {
    if s == nil {
        return "<nil server>"
    }
    return fmt.Sprintf("Server on port %d", s.Port)
}

func main() {
    var srv *Server
    // This does not panic because String checks for nil.
    fmt.Println(srv.String()) // Output: <nil server>

    // This would panic because it dereferences s without checking.
    // fmt.Println(srv.Port) // panic: invalid memory address or nil pointer dereference
}

This pattern is useful for logging or debugging. You can call fmt.Println(srv) on a nil pointer if the String method is safe. However, relying on this can hide bugs. If a method panics on nil, the caller must check. If a method handles nil, the caller might assume the pointer is valid when it is not. Document your nil behavior clearly.

Interface vs pointer nil

An interface can hold a nil pointer and still be non-nil. This is a frequent source of confusion. An interface is nil only if both its type and its value are nil. If you assign a nil pointer to an interface, the interface holds the type information, so it is not nil.

package main

import "fmt"

type Writer interface {
    Write([]byte) (int, error)
}

type File struct{}

func (f *File) Write(data []byte) (int, error) {
    return len(data), nil
}

func main() {
    var w Writer
    // w is nil. Both type and value are nil.
    fmt.Println(w == nil) // true

    var f *File
    // f is a nil pointer.
    w = f
    // w now holds the type *File and the value nil.
    // w is not nil.
    fmt.Println(w == nil) // false

    // Calling a method on w panics because the underlying pointer is nil.
    // w.Write([]byte{}) // panic: invalid memory address or nil pointer dereference
}

This distinction matters when you return interfaces from functions. If you return a nil pointer as an interface, the caller's nil check fails. Always return nil for the interface, not a typed nil pointer, unless you have a specific reason.

Convention aside: context.Context always goes as the first parameter, conventionally named ctx. Functions that take a context should respect cancellation and deadlines. If you pass a context through a chain of calls, ensure every function checks ctx.Err() or passes it down. Context is plumbing. Run it through every long-lived call site.

Decision matrix

Use a nil check when a function returns a pointer that might be missing.

Use initialization with &Type{} when you need a mutable value immediately.

Use a value type instead of a pointer when the value is never missing and never needs to be mutated by the caller.

Use go vet to catch obvious nil dereferences during development.

Use a default value when nil is not a meaningful state for your domain.

Use a method with a nil check when you want to support safe operations on uninitialized objects.

Use an interface return of nil when signaling absence, not a typed nil pointer.

Nil checks are cheap. Panics are expensive.

Where to go next