The go-slice-bounds-out-of-range error is a common runtime panic in Go applications, indicating an attempt to access an element of a slice at an index that does not exist within its current length. This error is a critical safety mechanism in Go, preventing memory corruption and unpredictable program behavior that could arise from accessing memory outside the bounds of an allocated slice. Understanding its causes and systematic debugging is crucial for robust Go development.
1. Symptoms: Clear description of indicators and shell output.
When a Go program encounters a slice bounds out of range error, it will immediately terminate with a runtime panic. The console output will typically include a clear error message, followed by a stack trace that pinpoints the exact location in the source code where the invalid access occurred.
A typical symptom looks like this:
panic: runtime error: slice bounds out of range [index] with length [length]
goroutine 1 [running]:
main.main()
/path/to/your/file.go:10 +0x2a
exit status 2
In this output:
panic: runtime error: slice bounds out of range [index] with length [length]explicitly states the problem, showing theindexthat was attempted and the actuallengthof the slice.- The
goroutine 1 [running]:section provides the stack trace, indicating the function (main.mainin this example) and the exact file path and line number (/path/to/your/file.go:10) where the panic originated. This information is vital for debugging. - The program will exit with a non-zero status code, typically
exit status 2, signaling an abnormal termination.
2. Root Cause: Technical explanation of the underlying cause.
The go-slice-bounds-out-of-range error occurs when a program tries to access an element of a slice using an index that falls outside the valid range of 0 to len(slice) - 1. This can also happen when attempting to create a sub-slice using indices that are invalid relative to the original slice’s length or capacity.
Specific scenarios leading to this error include:
-
Direct Indexing Out of Bounds:
- Accessing
s[i]wherei < 0. - Accessing
s[i]wherei >= len(s). - This is the most common cause, often due to off-by-one errors in loops or calculations.
- Accessing
-
Invalid Slicing Operations:
- Creating a sub-slice
s[low:high]wherelow < 0. - Creating a sub-slice
s[low:high]wherehigh > cap(s). Whilehighcan exceedlen(s)up tocap(s)when creating a new slice, accessing elements within that new slice still respects its new length. The panic occurs ifhighexceeds the underlying array’s capacity. - Creating a sub-slice
s[low:high]wherelow > high. This is an invalid range and will cause a panic.
- Creating a sub-slice
-
Empty or Nil Slices:
- Attempting to access an element of an empty slice (e.g.,
s[0]whenlen(s)is 0). - While accessing a
nilslice directly (e.g.,nilSlice[0]) often results in anil pointer dereferencepanic, certain operations or subsequent invalid re-slicing of anilslice can lead toslice bounds out of range.
- Attempting to access an element of an empty slice (e.g.,
-
Incorrect Loop Conditions:
- Using
for i := 0; i <= len(s); i++when iterating through elements, which attempts to accesss[len(s)]on the last iteration.
- Using
-
Concurrent Modifications:
- In rare cases, if a slice’s underlying array or length is modified concurrently by different goroutines without proper synchronization, it can lead to one goroutine having an outdated view of the slice’s bounds, resulting in an out-of-range access.
The core issue is a mismatch between the expected size or state of a slice and the actual index or range provided during an access operation.
3. Step-by-Step Fix: Accurate fix instructions. You MUST use “Before:” and “After:” labels for code comparison blocks.
Fixing slice bounds out of range errors primarily involves carefully checking and correcting the indices used to access or slice Go slices. The stack trace is your most valuable tool for pinpointing the exact line of code responsible.
-
Locate the Error: Use the file path and line number provided in the panic stack trace to identify the problematic line of code.
-
Inspect Slice Length and Index: At the identified line, determine the
len()of the slice in question and the value of the index being used. Ensure the indexisatisfies0 <= i < len(slice). -
Review Loop Conditions: If the error occurs within a loop, carefully examine the loop’s initialization, condition, and increment/decrement statements. Ensure they correctly iterate within the slice’s bounds.
-
Validate Slicing Operations: If the error is from a sub-slicing operation (e.g.,
slice[low:high]), verify that0 <= low <= high <= cap(slice). Remember thathighcan belen(slice)to include all elements, buthighcannot exceedcap(slice). Also,lowmust not be greater thanhigh. -
Handle Empty or Nil Slices: Before accessing elements, check if the slice is
nilor empty usingif s == nil || len(s) == 0.
Here are common scenarios and their fixes:
Scenario 1: Off-by-one error in a loop
Before:
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
// This loop condition is incorrect, it tries to access numbers[len(numbers)]
for i := 0; i <= len(numbers); i++ {
fmt.Println(numbers[i])
}
}
After:
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
// Correct loop condition: i must be strictly less than len(numbers)
for i := 0; i < len(numbers); i++ {
fmt.Println(numbers[i])
}
}
Scenario 2: Accessing an element beyond the slice’s current length
Before:
package main
import "fmt"
func main() {
data := make([]string, 3) // len is 3, cap is 3
data[0] = "apple"
data[1] = "banana"
// Attempting to access index 3, which is out of bounds (valid indices are 0, 1, 2)
fmt.Println(data[3])
}
After:
package main
import "fmt"
func main() {
data := make([]string, 3) // len is 3, cap is 3
data[0] = "apple"
data[1] = "banana"
// Corrected access to an existing index, or ensure slice is large enough
fmt.Println(data[2]) // Accessing the last valid element
// Or, if you intended to add an element, use append:
data = append(data, "cherry")
fmt.Println(data[3]) // Now index 3 is valid
}
Scenario 3: Invalid sub-slicing
Before:
package main
import "fmt"
func main() {
items := []string{"A", "B", "C", "D", "E"}
// Attempting to slice with high index exceeding capacity or low index greater than high
// Example 1: high index > cap(items)
// subSlice := items[0:10] // Panics if cap(items) < 10
// Example 2: low index > high index
subSlice := items[3:1] // Panics: slice bounds out of range [3:1]
fmt.Println(subSlice)
}
After:
package main
import "fmt"
func main() {
items := []string{"A", "B", "C", "D", "E"}
// Correct sub-slicing: 0 <= low <= high <= cap(items)
// Example 1: Corrected high index
subSlice1 := items[0:len(items)] // Valid, includes all elements
fmt.Println(subSlice1)
// Example 2: Corrected low and high indices
subSlice2 := items[1:3] // Valid, elements "B", "C"
fmt.Println(subSlice2)
}
4. Verification: How to confirm the fix works.
After implementing the fix, it’s crucial to verify that the error no longer occurs and that your program behaves as expected.
-
Run the Application: Execute your Go program. If the
panic: runtime error: slice bounds out of rangemessage no longer appears, it’s a good initial sign. -
Unit and Integration Tests: If you have unit tests, especially those covering the code path where the error occurred, run them. Create new tests or extend existing ones to specifically target edge cases related to slice lengths (e.g., empty slices, single-element slices, slices at maximum capacity) and index values.
-
Edge Case Testing: Manually or programmatically test the application with various inputs that might push the boundaries of slice access. This includes:
- Providing empty inputs that might result in empty slices.
- Providing inputs that create slices with a single element.
- Providing inputs that create very large slices.
- Testing scenarios where indices might be calculated to be zero or
len(slice) - 1.
-
Logging and Debugging: Add temporary
fmt.Printfstatements or use a debugger to inspect thelen()of the slice and the value of the index immediately before the problematic access. This can confirm that your corrections are leading to valid values at runtime.// Example of temporary logging for verification fmt.Printf("Slice length: %d, Attempted index: %d\n", len(mySlice), myIndex) // Then the access: value := mySlice[myIndex] -
Code Review: Have a colleague review the changes, paying close attention to loop conditions, index calculations, and slice operations. A fresh pair of eyes can often spot subtle errors.
By systematically verifying your changes, you can ensure the slice bounds out of range error is truly resolved and that no new, related issues have been introduced.
5. Common Pitfalls: Key mistakes to avoid.
While fixing slice bounds out of range errors often seems straightforward, several common pitfalls can lead to recurring issues or introduce new bugs.
-
Off-by-One Errors: This is by far the most frequent mistake. Developers often confuse
len(s)withlen(s)-1for the last valid index, or use<=instead of<in loop conditions. Always remember that valid indices for a slice of lengthNare0throughN-1. -
Ignoring Empty Slices: Failing to consider cases where a slice might be empty (
len(s) == 0). Attempting to accesss[0]on an empty slice will always result in a panic. Always checklen(s) > 0before accessing elements if the slice might be empty. -
Confusing
lenandcap:len(s)is the number of elements currently in the slice, whilecap(s)is the maximum number of elements the underlying array can hold without reallocation. While you can re-slice a slice up to its capacity (e.g.,s = s[:cap(s)]), you can only access elements up tolen(s)-1. Attempting to accesss[len(s)]will panic, even iflen(s) < cap(s). -
Incorrect Re-slicing: When re-slicing a slice, ensure the new
lowandhighindices are valid relative to the original slice’s bounds. For example,s = s[i:]will panic ifiis greater thanlen(s). -
Not Checking for
nilSlices: Whilenilslice access often results in anil pointer dereference, it’s good practice to check fornilbefore any slice operations, especially if the slice might not have been initialized. Anilslice has a length and capacity of 0, solen(nilSlice)is valid and returns 0, butnilSlice[0]is not. -
Concurrent Modifications Without Synchronization: In concurrent Go programs, if multiple goroutines are modifying the same slice (e.g., appending elements, re-slicing) without proper synchronization mechanisms (like mutexes), one goroutine might read an outdated length or capacity, leading to an out-of-bounds access.
-
Hardcoding Indices: Relying on magic numbers for indices instead of dynamically calculating them based on
len(slice)can make code brittle and prone to errors if the slice’s size changes.
By being mindful of these common pitfalls, developers can write more robust Go code that gracefully handles slice operations and avoids runtime panics.
6. Related Errors: 2-3 similar errors.
Understanding errors similar to go-slice-bounds-out-of-range can help in quickly diagnosing and resolving related issues in Go programs. These errors often stem from similar underlying causes related to invalid memory access or incorrect indexing.
-
go-index-out-of-range(for arrays): This error is conceptually identical toslice bounds out of rangebut specifically applies to fixed-size arrays in Go. Arrays have a fixed length determined at compile time. If you attempt to access an element of an array using an index outside its0tolength-1range, you will encounter this panic.- Example:
var arr [5]int; fmt.Println(arr[5]) - Distinction: Slices are dynamic views into underlying arrays, while arrays are fixed-size data structures. The panic message will typically specify “array index out of range” or “index out of range” without mentioning “slice bounds.”
- Example:
-
go-nil-pointer-dereference: This panic occurs when you attempt to access a method or field of anilpointer. While not directly about slice bounds, it’s highly related because anilslice is effectively anilpointer to an underlying array. If you try to perform operations that implicitly dereference anilslice (e.g.,nilSlice[0]withoutlencheck, or calling a method on a struct containing anilslice without checking the struct field), you might encounter this error before or instead of aslice bounds out of rangeerror.- Example:
var s []int; fmt.Println(s[0])(This often results innil pointer dereferenceif the slice is trulyniland not just empty, depending on the Go version and specific operation). - Distinction: This error specifically indicates that a pointer variable holds a
nilvalue, and you’re trying to use it as if it points to a valid object.slice bounds out of rangeimplies the slice itself is valid (notnil), but the index is wrong for its current length.
- Example:
-
go-array-index-out-of-bounds: This is another phrasing forgo-index-out-of-rangewhen dealing with arrays. It explicitly states that the index used to access an array element is outside the valid range. The underlying cause and resolution are the same asgo-index-out-of-range.- Example:
var data [3]byte; data[3] = 'x' - Distinction: This is essentially a synonym for
go-index-out-of-rangebut emphasizes the “array” context. The core problem remains an invalid index for a fixed-size collection.
- Example:
These related errors highlight a common theme in Go: strict runtime checks for memory access safety. By understanding the nuances of each, developers can more effectively debug and prevent such panics.