Go / Gin

How to Fix "panic: runtime error: invalid memory address or nil pointer dereference" in Go

4 min read by DebuggedIt

Quick answer

Your Go program crashes at runtime with a panic pointing at memory address 0x0 instead of running normally. This means somewhere in your code, you're calling a...

Your Go program crashes at runtime with a panic pointing at memory address 0x0 instead of running normally. This means somewhere in your code, you're calling a method, accessing a field, or indexing into something that's nil β€” a pointer, map, slice, interface, or channel that was never initialized.

The Problem

The panic shows up with a stack trace pointing at the exact line, but the underlying cause (what's nil) isn't always obvious from the message alone:

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

goroutine 1 [running]:
main.(*User).GetName(...)
	/app/main.go:24
main.main()
	/app/main.go:38 +0x1b2
exit status 2

It's also common when working with maps that were declared but never initialized:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/app/main.go:15 +0x65

Why It Happens

Go's zero value for pointers, maps, slices, interfaces, channels, and functions is nil, and the language doesn't stop you from declaring a variable and using it before it's actually assigned a real value. The panic fires when you dereference a nil pointer to read a field or call a method with a pointer receiver, when the underlying value hasn't been set. Common causes:

  • A struct field of pointer type was never assigned before you accessed it: var user *User; user.Name.
  • A function that's supposed to return a valid pointer instead returned nil on an error path, and the caller didn't check the error before using the result.
  • A map was declared with var m map[string]int instead of make(map[string]int), which leaves it nil and unable to accept writes.
  • An interface holds a typed nil value β€” a subtle Go gotcha where an interface variable isn't itself nil even though the underlying pointer it holds is.
  • A struct returned from a database query or JSON unmarshal didn't get populated because of an earlier silent error.

The Fix

Start by reading the stack trace bottom to top to find the exact function and line where the dereference happened. In the example above, that's GetName at main.go:24. Check whether the receiver or field involved was ever assigned:

func (u *User) GetName() string {
    return u.Name // panics if u is nil
}

Add a nil check before accessing fields on a pointer that might not be set:

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

For the nil map case, always initialize maps with make before writing to them:

userCache := make(map[string]int)
userCache["alice"] = 1

If the nil value is coming from a function's return value, check the error before touching the result β€” this is the single most common source of this panic in real Go services:

user, err := repo.FindByID(id)
if err != nil {
    return nil, err
}
fmt.Println(user.Name) // safe: err was checked first

For the typed-nil-interface gotcha specifically, compare against the concrete nil type rather than assuming an interface check catches it:

var p *User = nil
var i interface{} = p
fmt.Println(i == nil) // false β€” i holds a typed nil *User, not an untyped nil

Still Not Working?

If the panic happens deep inside a goroutine and crashes the whole process instead of just failing one request, wrap goroutine entry points with a deferred recover so one nil dereference doesn't take down your entire service:

func safeGo(fn func()) {
    go func() {
        defer func() {
            if r := recover(); r != nil {
                log.Printf("recovered from panic: %v", r)
            }
        }()
        fn()
    }()
}

In a Gin handler specifically, add the built-in recovery middleware so a nil dereference in one request returns a 500 instead of crashing the entire server for every in-flight request:

router := gin.New()
router.Use(gin.Recovery())

For tracking down harder-to-reproduce cases, run your test suite with the race detector and add targeted nil checks around struct fields populated from external sources like database rows, JSON payloads, or third-party API responses, since those are the most common origin points for unexpected nils in production.