TL;DR: The Quick Fix
This crash occurs because you declared a map variable but never allocated memory for it. Go allows you to read from a nil map, but writing to one triggers an immediate panic.
The Wrong Way:
var counts map[string]int
counts["users"] = 10 // ❌ PANIC HERE
The Right Way (using make):
counts := make(map[string]int)
counts["users"] = 10 // ✅ Works
The Right Way (using a literal):
counts := map[string]int{}
counts["users"] = 10 // ✅ Works
Why Does This Happen?
Maps in Go are reference types. When you write var m map[K]V, the initial value of m is nil. It doesn't point to an initialized hash table yet.
Go is surprisingly lenient with nil maps during read operations. If you try to access a key in a nil map, Go simply returns the zero value for that type (like 0 for integers or "" for strings). However, the runtime draws a hard line at writes. It won't automatically allocate memory when you assign a value. Instead, it halts the program to prevent memory corruption.
Common Pitfalls and Solutions
1. The Uninitialized Global or Local Variable
This is the most common mistake. You define a map to track something—like HTTP response codes—but skip the initialization step.
func trackStatus() {
var statusCodes map[int]string
// ... logic happens
statusCodes[200] = "OK" // Error: assignment to entry in nil map
}
The Fix: Always use make(). If you know you'll store roughly 100 items, you can even pre-allocate space with make(map[int]string, 100) to improve performance.
2. Maps Inside Structs
Initializing a struct does not automatically initialize any maps contained within it. This is a frequent source of bugs in larger applications.
type UserProfile struct {
ID int
Settings map[string]string
}
func main() {
u := UserProfile{ID: 1}
u.Settings["theme"] = "dark" // ❌ PANIC: Settings is nil
}
The Fix: Use a constructor function. This pattern ensures your struct is always ready for use immediately after creation.
func NewUserProfile(id int) *UserProfile {
return &UserProfile{
ID: id,
Settings: make(map[string]string),
}
}
3. Lazy Initialization for JSON
The encoding/json package is smart. If you unmarshal JSON data into a nil map, Go initializes it for you. But if the incoming JSON is empty or the unmarshaling fails, the map remains nil.
var data map[string]interface{}
// If json.Unmarshal isn't called or fails, data stays nil
err := json.Unmarshal(rawBytes, &data)
// Logic that assumes 'data' is ready will crash here
data["updated_at"] = time.Now().Unix()
How to Verify and Prevent This
To keep your code stable, follow these three practical steps:
- **Run Static Analysis:** Tools like `go vet` or `golangci-lint` can often spot uninitialized maps before you even run your code.
- **Defensive Nil Checks:** If a function receives a map from an external source, check it. A simple `if m == nil { m = make(map[K]V) }` can save your production environment from a crash.
- **Unit Tests:** Write tests that simulate empty inputs. If your logic tries to write to an uninitialized map, the test will fail loudly.
Summary Table
Operation
Result on Nil Map
`val := m["key"]`
Safe (returns zero value)
`len(m)`
Safe (returns 0)
`delete(m, "key")`
Safe (does nothing)
`m["key"] = "val"`
**Panic (Crash)**

