Fixing the Go Panic: runtime error: index out of range [X] with length Y

beginner🔷 Go2026-04-01| Any Go environment (Linux, macOS, Windows), Go 1.x+

Error Message

panic: runtime error: index out of range [X] with length Y
#go#panic#debugging#slices#error-handling

The Crash ScenarioPicture this: you're testing a new feature, you hit run, and your terminal suddenly explodes with a nasty stack trace. Right at the top, you see the dreaded message:

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

This usually crops up when your code tries to grab an item from a slice, array, or string that isn't there. Go uses 0-based indexing. If you have a list of 3 users, they live at positions 0, 1, and 2. Trying to access index 3 is like asking for the fourth person in a three-person line—it's impossible, so Go pulls the emergency brake to keep your memory safe.

A Classic Mistake: Command-Line ArgumentsTake a look at this common slip-up where a developer assumes the user will always provide input:

package main

import (
	"fmt"
	"os"
)

func main() {
	// We're grabbing the first argument provided by the user
	arg := os.Args[1]
	fmt.Println("Argument provided:", arg)
}

If you run go run main.go without passing any extra words, os.Args only contains one thing: the program name itself at index 0. Because index 1 is empty, the program crashes instantly with index out of range [1] with length 1.

Decoding the PanicThe error message gives you the exact coordinates of the failure:

  • [X]: The specific index your code tried to reach.- length Y: How many items were actually in the collection.The math is simple: your index must be less than the length. While languages like Python might throw an IndexError or JavaScript might just return undefined, Go is much stricter. It halts execution to prevent unpredictable behavior or memory corruption.

The Quick Fix: Guard Your AccessThe fastest way to stop the bleeding is a simple length check. Never touch an index unless you've confirmed it exists first.

package main

import (
	"fmt"
	"os"
)

func main() {
	// Check if the user actually provided the argument
	if len(os.Args) > 1 {
		arg := os.Args[1]
		fmt.Println("Argument provided:", arg)
	} else {
		fmt.Println("Error: You forgot to provide an argument!")
	}
}

By adding len(os.Args) > 1, you've built a safety gate. The code inside the if block only runs when it's safe to do so.

Better Strategies for Resilient CodeManual checks are fine for one-offs, but they can make your code messy if overused. Here is how to handle data more idiomatically.

1. Let 'for range' Do the Heavy LiftingAvoid manual index variables whenever possible. The range keyword is the gold standard for iterating in Go because it's physically impossible for it to go out of bounds.

The Risky Way:

items := []string{"apple", "banana", "cherry"}
for i := 0; i <= len(items); i++ { // The '<=' is an off-by-one error waiting to happen
	fmt.Println(items[i])
}

The Go Way:

items := []string{"apple", "banana", "cherry"}
for _, item := range items {
	fmt.Println(item)
}

2. Safer SlicingWhen you take a sub-slice, like data[0:10], Go will panic if the original data has fewer than 10 elements. If you're dealing with dynamic data—like a 500-line CSV where some rows might be short—always cap your indices.

func getPreview(data []int, limit int) []int {
	if limit > len(data) {
		limit = len(data)
	}
	return data[0:limit]
}

3. Defensive StartsAssume your slices might be empty. If your function relies on data[0], check the length right at the top of the function and return early if there's nothing to process.

How to Verify the FixDon't just assume it's fixed. Put your code through its paces:

  • Test the Edges: If your code processes a list, what happens if the list is empty? What if it has exactly one item?- Unit Testing: Write a test case that passes a nil or empty slice to your function to ensure it handles the vacuum gracefully.- Use Static Analysis: Run go vet ./.... It won't catch every runtime error, but it's great at spotting obvious "off-by-one" mistakes in your loops.Moving from manual indexing to range loops and guard clauses turns fragile scripts into production-ready software.

Related Error Notes