Go / Gin

Go "Nil Pointer Dereference" Panic, How to Debug

A nil pointer dereference panic means your code tried to access a field or method on a pointer that was never actually assigned a valid value — Go's type system doesn't prevent this at compile time, so the panic happens at runtime, exactly at the moment the nil pointer is used.

The Problem

Your program crashes with:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47b2a1]

goroutine 1 [running]:
main.(*User).GetName(...)
    /app/main.go:15
main.main()
    /app/main.go:25 +0x1a5

The stack trace points to a specific line, but it's not always obvious at a glance which variable was actually nil at that point.

Pointer with no allocation var u *User u == nil u.Name nothing here panic

Why It Happens

Go allows a pointer variable to exist without pointing at anything (its zero value is nil), and accessing a field or calling a method that dereferences that pointer without checking first causes the panic. Common patterns:

  • A struct field that's itself a pointer to another struct was never initialized, and code accesses a field on it assuming it was
  • A function that's supposed to return a populated pointer returns nil under an error condition, but the caller doesn't check the error before using the returned value
  • A map lookup for a struct pointer returns the zero value (nil for pointer types) when the key doesn't exist, and the code proceeds to use it without checking
  • An interface holding a nil pointer of a concrete type is itself non-nil as an interface, leading to confusing situations where a nil check on the interface passes, but using it still panics

The Fix

First, read the stack trace carefully — it identifies the exact function and line where the panic occurred, which is the starting point for tracing back which variable was nil:

main.(*User).GetName(...)
    /app/main.go:15

Open that exact line and identify which pointer is being dereferenced. Add an explicit nil check before using it, returning an appropriate error or default instead of proceeding:

func (u *User) GetName() string {
    if u == nil {
        return ""
    }
    return u.Name
}

If the nil value is coming from a function's return, make sure you're checking the error before using the returned value, rather than assuming a non-error return always means a valid pointer:

user, err := fetchUser(id)
if err != nil {
    return err
}
// safe to use user here, since err == nil implies user should be valid
fmt.Println(user.Name)

For map lookups, use the two-value form to explicitly check whether the key existed, rather than assuming a returned pointer is always valid:

user, ok := userMap[id]
if !ok {
    return errors.New("user not found")
}
fmt.Println(user.Name)

For the nil-interface-holding-a-nil-pointer situation, be aware that a variable of interface type holding a nil pointer of a concrete type is not itself equal to nil when compared directly, which can be genuinely surprising:

var u *User = nil
var i interface{} = u
fmt.Println(i == nil) // false, even though u is nil

This happens because an interface value consists of both a type and a value, and here the type is set (*User) even though the value is nil — so the interface itself isn't considered nil. This is a well-known Go gotcha worth being aware of specifically when passing typed nil pointers through interface-typed parameters or return values.

Still Not Working?

If the panic happens deep inside a goroutine rather than your main execution path, make sure you have proper panic recovery in place for goroutines specifically, since an unrecovered panic in any goroutine crashes the entire program, not just that goroutine:

func safeGoroutine() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("recovered from panic: %v", r)
        }
    }()
    // goroutine logic here
}

This doesn't fix the underlying nil pointer issue, but it prevents one goroutine's bug from taking down your entire application while you locate and fix the actual root cause using the recovered panic's logged details.