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
| Expression | Value | Safe to index? |
|---|---|---|
s[0] | first element | yes |
s[2] | last element | yes |
s[3] | beyond length | panic |
s[:3], s[0:3] | full-slice form | yes |
s[0:10] | slice past length | panic |
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
- Fix "nil pointer dereference" Panic in Go
- Most Common Golang Errors and How to Fix Them
- go mod tidy Explained