Understanding the Panic

The index out of range panic occurs when you access a slice or array element at an invalid position:

``` panic: runtime error: index out of range [5] with length 3

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

The message shows the invalid index you tried to access (5) and the actual length (3).

Common Scenarios and Fixes

Scenario 1: Direct Index Access

Problem code: ``go func getItem(items []string, index int) string { return items[index] // Panics if index >= len(items) }

Safe version: ``go func getItem(items []string, index int) (string, error) { if index < 0 || index >= len(items) { return "", fmt.Errorf("index %d out of bounds for slice of length %d", index, len(items)) } return items[index], nil }

Scenario 2: Loop with Fixed Index

Problem code: ``go func processBatch(records []Record) { for i := 0; i <= len(records); i++ { // Bug: <= should be < process(records[i]) } }

Fixed version: ``go func processBatch(records []Record) { for i := 0; i < len(records); i++ { // Correct: < process(records[i]) } }

Or use range for clarity: ``go func processBatch(records []Record) { for _, record := range records { process(record) } }

Scenario 3: Slicing Beyond Capacity

Problem code: ``go data := []int{1, 2, 3} subset := data[1:5] // Panic: slice bounds out of range [1:5]

Safe slicing: ``go func safeSlice(data []int, start, end int) []int { if start < 0 { start = 0 } if end > len(data) { end = len(data) } if start >= end { return []int{} } return data[start:end] }

Or use Go 1.21's min function: ``go data := []int{1, 2, 3} end := min(5, len(data)) subset := data[1:end] // [2, 3]

Scenario 4: Accessing After Append

This is a subtle bug that can cause issues:

Problem code: ```go func main() { items := make([]int, 0, 5) items = append(items, 1, 2, 3)

// Later, someone forgets items was re-allocated fmt.Println(items[5]) // Panic! } ```

Always check length: ```go func main() { items := make([]int, 0, 5) items = append(items, 1, 2, 3)

if len(items) > 5 { fmt.Println(items[5]) } else { fmt.Println("Index not available") } } ```

Scenario 5: Empty Slice Confusion

Problem code: ``go var results []string // nil slice if results[0] == "" { // Panic! // handle empty }

Fixed version: ``go var results []string if len(results) == 0 { // handle empty }

Safe Access Helpers

Create reusable safe access functions:

```go // SafeSliceGet returns the element at index or a default value func SafeSliceGet[T any](slice []T, index int, defaultValue T) T { if index < 0 || index >= len(slice) { return defaultValue } return slice[index] }

// SafeSliceGetOK returns the element and a boolean indicating success func SafeSliceGetOK[T any](slice []T, index int) (T, bool) { var zero T if index < 0 || index >= len(slice) { return zero, false } return slice[index], true }

// Usage items := []string{"a", "b", "c"} first := SafeSliceGet(items, 0, "default") // "a" fifth := SafeSliceGet(items, 5, "default") // "default"

val, ok := SafeSliceGetOK(items, 1) // "b", true val, ok = SafeSliceGetOK(items, 10) // "", false ```

Debugging Commands

When you see the panic, the stack trace shows exactly where it happened:

```bash # Run with full stack trace GOTRACEBACK=all go run main.go

# Use delve for interactive debugging dlv debug main.go (dlv) break main.go:15 (dlv) continue (dlv) print len(items) (dlv) print index ```

Verification Tests

Write tests that specifically check boundary conditions:

```go func TestGetItem(t *testing.T) { items := []string{"a", "b", "c"}

// Valid indices for i := 0; i < len(items); i++ { got, err := getItem(items, i) if err != nil { t.Errorf("unexpected error at index %d: %v", i, err) } if got != items[i] { t.Errorf("expected %s, got %s", items[i], got) } }

// Invalid indices invalidIndices := []int{-1, len(items), len(items) + 1, 100} for _, idx := range invalidIndices { _, err := getItem(items, idx) if err == nil { t.Errorf("expected error at index %d", idx) } } } ```

Prevention with Linters

Use static analysis to catch potential issues:

```bash # Install and run staticcheck go install honnef.co/go/tools/cmd/staticcheck@latest staticcheck ./...

# Install gosec for security-related bounds checks go install github.com/securego/gosec/v2/cmd/gosec@latest gosec ./... ```

These tools can identify code paths where bounds checking might be missing.