Fix Go Panic: "index out of range" Runtime Error (2026)

Go panic: "index out of range"

The immediate fix: check len() before each index access. Replace a bare s[i] with:

if i < len(s) {
    v := s[i]
}

The Full Panic Output

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

goroutine 1 [running]:
main.main()
        /home/user/project/main.go:7 +0x1d
exit status 2

Read the bracketed numbers as a pair: your code asked for index [5], but the value held only 3 elements. The stack frame below names the file and line — here main.go:7 is where the bad index hit.

Minimal Reproduction

package main

import "fmt"

func main() {
    s := []string{"a", "b", "c"}
    fmt.Println(s[5]) // panic at this line
}

The panic happens at run time, not at compile time, because Go cannot always know a slice length in advance.

Why Go Panics Here

Out-of-bounds reads would otherwise return garbage from adjacent memory. The Go spec makes constant indices a compile error when the bound check can fail at compile time. Every other violation turns into a run-time panic. There is no undefined behavior for slices or arrays; you get a clean crash instead.

len vs cap

Two functions describe a slice, and only one guards access by index:

s := make([]int, 3, 10)
len(s) // 3  -> valid indexes are 0..2
cap(s) // 10 -> room before the next allocation, NOT addressable
ExpressionValueSafe to index?
s[0]first elementyes
s[2]last elementyes
s[3]beyond lengthpanic
s[:3], s[0:3]full-slice formyes
s[0:10]slice past lengthpanic

A common mistake: code allocates with make([]int, 0, 10) and then writes s[9] = 1. The capacity is 10, but the length is still 0. Append instead:

s := make([]int, 0, 10)
s = append(s, 1) // length grows to 1

Defensive Patterns

Pattern 1: Guard every external index

Any index from user input, config, or an API needs a bounds check:

func pick(items []string, i int) (string, bool) {
    if i < 0 || i >= len(items) {
        return "", false
    }
    return items[i], true
}

Pattern 2: Range over the slice

A range loop never goes out of bounds, because it walks the actual length:

for i, v := range items {
    fmt.Println(i, v)
}

Prefer this over for i := 0; i <= len(items); i++ — the <= there is an off-by-one bug that panics on the last pass.

Pattern 3: Clamp before you slice

Slice expressions also panic when the high bound passes the length. Clamp it:

end := i + pageSize
if end > len(items) {
    end = len(items)
}
page := items[i:end]

Pattern 4: Empty-check before element zero

An empty or nil slice has length 0, so even s[0] panics. Test first:

if len(items) == 0 {
    return nil
}
first := items[0]

A call to len(nil) is safe and returns 0, so one check covers both cases.

FAQ

Why does the message say "with length" but my value was a map?

Map reads never panic on absent keys; they return the zero value. This error text comes only from arrays, slices, strings, and multi-dimensional combinations of them. For maps, use the comma-ok form instead.

Can I recover from this panic?

Yes, with a deferred recover(), but treat that as a last resort. A check on the bounds expresses intent and costs almost nothing. Recovery can hide real bugs in libraries such as nil pointer panics, which share this stack-trace format.

Related Articles

Sources

Leonardo Lazzaro

Software engineer and technical writer. 10+ years experience in DevOps, Python, and Linux systems.

More articles by Leonardo Lazzaro