Home › Go

panic: assignment to entry in nil map — how to fix

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/home/me/app/main.go:7 +0x2c
exit status 2

Quick fix: Initialise the map before writing: m := make(map[string]int) or m := map[string]int{}. For struct fields, initialise them in a constructor (func NewX() *X { return &X{items: map[string]int{}} }) or lazily (if s.items == nil { s.items = make(map[string]int) }). Nested maps need each inner map created too.

What it means

The zero value of a map type in Go is nil. A nil map behaves like an empty map for reading — len(m) is 0, m["x"] returns the zero value, range does nothing, delete is a no-op — but writing to it panics at runtime, because there is no hash table allocated to store the entry.

var m map[string]int   // nil
fmt.Println(m["a"])    // 0, fine
m["a"] = 1             // panic: assignment to entry in nil map

The stack trace shows the file and line of the write.

Fix 1: create the map with make or a literal

m := make(map[string]int)          // empty, ready to use
m := make(map[string]int, 100)     // with a capacity hint
m := map[string]int{}              // same as make, literal form
m := map[string]int{"a": 1}        // pre-filled

var m map[string]int alone only declares it.

Cause 2: map field in a struct

Struct fields get zero values, so a map field is nil until you set it:

type Cache struct {
    items map[string]string
}

c := Cache{}            // or &Cache{}, or var c Cache
c.items["k"] = "v"      // panic

Fix with a constructor:

func NewCache() *Cache {
    return &Cache{items: make(map[string]string)}
}

Or initialise lazily in the method that writes, which also makes the zero value usable:

func (c *Cache) Set(k, v string) {
    if c.items == nil {
        c.items = make(map[string]string)
    }
    c.items[k] = v
}

Lazy init must use a pointer receiver (*Cache), otherwise you initialise a copy. If several goroutines call it, protect it with a sync.Mutex (or use sync.Map) — concurrent map writes cause a separate fatal error.

Cause 3: nested maps

Creating the outer map doesn't create the inner ones:

graph := make(map[string]map[string]int)
graph["a"]["b"] = 1     // panic: graph["a"] is nil

Create the inner map on first use:

inner, ok := graph["a"]
if !ok {
    inner = make(map[string]int)
    graph["a"] = inner
}
inner["b"] = 1

Often a composite key is simpler: map[[2]string]int or a struct key map[edge]int.

Cause 4: maps inside slices

rows := make([]map[string]any, 3)   // three nil maps
rows[0]["id"] = 1                   // panic

Fill each element: for i := range rows { rows[i] = map[string]any{} }, or build with append(rows, map[string]any{"id": 1}).

Cause 5: a function returned a nil map

A function that returns nil on an early path (return nil, err) and a caller that ignores the error and writes to the result. Check the error, or have the function return an empty map instead of nil.

Note that json.Unmarshal into a struct allocates maps when the JSON has the key; if the key is missing, the field stays nil. Initialise it before writing to it.

Finding it

Run with the race detector and full traces when it's intermittent:

go run -race .
GOTRACEBACK=all go run .

The top frame of the panic is the exact line; the variable being indexed there is nil. go vet doesn't catch this, but staticcheck sometimes flags obvious cases (SA5000).

Last updated 2026-11-06.