8.8 KiB
8.8 KiB
Go Error Patterns
Common Go errors with diagnosis and solutions.
Nil Pointer Errors
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation]
Causes:
- Calling method on nil pointer
- Accessing field of nil struct
- Dereferencing nil pointer
Solutions:
// Check for nil before use
if user != nil {
fmt.Println(user.Name)
}
// Return early on nil
func processUser(user *User) error {
if user == nil {
return errors.New("user is nil")
}
// proceed
}
// Use zero values where appropriate
type Config struct {
Timeout time.Duration
}
func (c *Config) GetTimeout() time.Duration {
if c == nil {
return 30 * time.Second // default
}
return c.Timeout
}
Slice/Array Errors
panic: runtime error: index out of range
panic: runtime error: index out of range [5] with length 3
Causes:
- Accessing index beyond slice length
- Empty slice access
- Off-by-one error
Solutions:
// Check length first
if len(items) > index {
item := items[index]
}
// Safe first/last element
func first(items []string) (string, bool) {
if len(items) == 0 {
return "", false
}
return items[0], true
}
// Use range for iteration
for i, item := range items {
// safe access
}
panic: runtime error: slice bounds out of range
panic: runtime error: slice bounds out of range [:5] with length 3
Solutions:
// Validate slice bounds
func safeSlice(s []int, start, end int) []int {
if start < 0 {
start = 0
}
if end > len(s) {
end = len(s)
}
if start > end {
return nil
}
return s[start:end]
}
Map Errors
panic: assignment to entry in nil map
panic: assignment to entry in nil map
Causes:
- Writing to uninitialized map
- Map declared but not made
Solutions:
// Wrong
var m map[string]int
m["key"] = 1 // panic!
// Correct - use make
m := make(map[string]int)
m["key"] = 1
// Or initialize with literal
m := map[string]int{}
m["key"] = 1
// In structs, initialize in constructor
type Cache struct {
data map[string]string
}
func NewCache() *Cache {
return &Cache{
data: make(map[string]string),
}
}
Map access returns zero value
value := m["nonexistent"] // Returns zero value, not error
Solutions:
// Check if key exists
value, ok := m["key"]
if !ok {
// key doesn't exist
}
// Or use default
func getOrDefault(m map[string]int, key string, def int) int {
if v, ok := m[key]; ok {
return v
}
return def
}
Channel Errors
fatal error: all goroutines are asleep - deadlock!
fatal error: all goroutines are asleep - deadlock!
Causes:
- Channel send/receive with no corresponding operation
- Unbuffered channel blocking
- Waiting on channel that's never written to
Solutions:
// Wrong - unbuffered channel blocks
ch := make(chan int)
ch <- 1 // blocks forever, no receiver
// Correct - use goroutine
ch := make(chan int)
go func() {
ch <- 1
}()
value := <-ch
// Or use buffered channel
ch := make(chan int, 1)
ch <- 1 // doesn't block
value := <-ch
// Always close channels when done sending
go func() {
defer close(ch)
for _, item := range items {
ch <- item
}
}()
panic: send on closed channel
panic: send on closed channel
Solutions:
// Only sender should close channel
// Use sync.Once for safe closing
var once sync.Once
closeCh := func() {
once.Do(func() {
close(ch)
})
}
// Or use context for cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
for {
select {
case <-ctx.Done():
return
case ch <- value:
}
}
}()
Interface Errors
panic: interface conversion: X is nil, not Y
panic: interface conversion: interface {} is nil, not string
Solutions:
// Use type assertion with ok
value, ok := i.(string)
if !ok {
// handle non-string or nil
}
// Use type switch
switch v := i.(type) {
case string:
fmt.Println("string:", v)
case int:
fmt.Println("int:", v)
case nil:
fmt.Println("nil")
default:
fmt.Println("unknown type")
}
panic: interface conversion: X is Y, not Z
panic: interface conversion: interface {} is int, not string
Same solution - always use type assertion with ok check.
Concurrency Errors
fatal error: concurrent map writes
fatal error: concurrent map writes
Causes:
- Multiple goroutines writing to same map
- No synchronization
Solutions:
// Option 1: Use sync.Mutex
type SafeMap struct {
mu sync.RWMutex
m map[string]int
}
func (s *SafeMap) Set(key string, value int) {
s.mu.Lock()
defer s.mu.Unlock()
s.m[key] = value
}
func (s *SafeMap) Get(key string) (int, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.m[key]
return v, ok
}
// Option 2: Use sync.Map
var m sync.Map
m.Store("key", 1)
value, ok := m.Load("key")
data race detected
WARNING: DATA RACE
Write by goroutine X:
...
Previous read by goroutine Y:
...
Solutions:
// Use mutex for shared state
var (
mu sync.Mutex
count int
)
func increment() {
mu.Lock()
count++
mu.Unlock()
}
// Or use atomic operations
var count int64
func increment() {
atomic.AddInt64(&count, 1)
}
// Run with race detector
// go run -race main.go
// go test -race ./...
Import/Module Errors
cannot find package
cannot find package "github.com/user/repo" in any of:
/usr/local/go/src/github.com/user/repo (from $GOROOT)
/home/user/go/src/github.com/user/repo (from $GOPATH)
Solutions:
# Initialize go modules
go mod init myproject
# Download dependencies
go mod tidy
# Or get specific package
go get github.com/user/repo
module declares its path as X but was required as Y
module declares its path as: github.com/old/path
but was required as: github.com/new/path
Solutions:
# Update go.mod with replace directive
# go.mod
replace github.com/old/path => github.com/new/path v1.0.0
# Or update import paths in code
Error Handling Patterns
Wrapping Errors (Go 1.13+)
// Wrap with context
if err != nil {
return fmt.Errorf("failed to process user %d: %w", userID, err)
}
// Unwrap to check original error
if errors.Is(err, sql.ErrNoRows) {
// handle not found
}
// Get specific error type
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("Path:", pathErr.Path)
}
Custom Errors
// Sentinel errors
var ErrNotFound = errors.New("not found")
// Custom error type
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
// Check with errors.As
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Printf("Validation failed on %s\n", valErr.Field)
}
Build Errors
undefined: X
./main.go:10:2: undefined: someFunction
Causes:
- Function/variable not defined
- Wrong package imported
- Unexported name (lowercase)
Solutions:
// Check export - must be uppercase
func PublicFunction() {} // Exported
func privateFunction() {} // Not exported
// Check import
import "mypackage"
mypackage.PublicFunction()
imported and not used
./main.go:4:2: "fmt" imported and not used
Solutions:
// Remove unused import
// Or use blank identifier if needed for side effects
import _ "database/sql/driver"
// Use goimports to auto-fix
// goimports -w .
declared and not used
./main.go:10:2: x declared and not used
Solutions:
// Use the variable or remove it
// Use blank identifier if intentionally unused
_ = someFunction()
// For error checking
result, _ := riskyFunction() // Intentionally ignore error (not recommended)
Quick Reference Table
| Error | Category | Quick Fix |
|---|---|---|
| nil pointer dereference | Nil | Check for nil before use |
| index out of range | Slice | Check len() first |
| assignment to nil map | Map | Use make(map[K]V) |
| deadlock | Channel | Ensure send/receive pairs match |
| send on closed channel | Channel | Only sender closes |
| interface conversion nil | Interface | Use type assertion with ok |
| concurrent map writes | Concurrency | Use sync.Mutex or sync.Map |
| data race | Concurrency | Add synchronization, use -race |
| cannot find package | Module | Run go mod tidy |
| undefined | Build | Check export (uppercase) |