Debugging Basics
Prerequisites: Comments & gofmt
Go problems come in two kinds: compile errors, which stop the program from building, and runtime panics, which crash a program that built successfully.
Reading compile errors
Every compile error names the file, line, and column:
# example.com/hello
./main.go:6:5: declared and not used: x
./main.go:9:5: append(nums, 2) (value of type []int) is not used
main.go:6:5 means line 6, column 5. Fix the first error
first — later ones are sometimes caused by it. Go's compiler is
strict (unused variables and imports are errors), but its
messages are usually direct about what's wrong.
Printf debugging
The quickest way to see what a program is doing is to print
values at key points. %+v shows struct field names, %T shows
the type, and %q makes empty or whitespace-only strings
visible:
package main
import "fmt"
type Order struct {
ID int
Total float64
}
func main() {
order := Order{ID: 7, Total: 42.5}
name := " "
fmt.Printf("DEBUG order=%+v type=%T name=%q\n", order, order, name)
}
DEBUG order={ID:7 Total:42.5} type=main.Order name=" "
Remove these lines once the bug is fixed.
Panics and stack traces
A panic is Go's runtime crash — caused by things like indexing
past the end of a slice, dereferencing a nil pointer, or writing
to a nil map. Go prints the reason and a stack trace:
panic: runtime error: index out of range [5] with length 3
goroutine 1 [running]:
main.main()
/home/you/hello/main.go:8 +0x1d
exit status 2
Read it top-down: the first line says what went wrong, and
the first line under goroutine 1 that points into your code
(main.go:8) says where. In longer traces, skip frames from
the standard library until you reach one of your own files.
Using a debugger: Delve
Delve (dlv) is the standard Go debugger. The VS Code Go
extension uses it behind the scenes, so you can usually just click
in the margin to set a breakpoint and press F5. From a terminal:
go install github.com/go-delve/delve/cmd/dlv@latest
dlv debug # build and start the program under the debugger
(dlv) break main.go:8
(dlv) continue
(dlv) print nums
(dlv) next
(dlv) quit
Common mistakes
- Reading only the last line of a long error output — the useful information is usually at the top.
- Leaving debug
Printfcalls in finished code. - Treating a panic as random: it always points to a specific line and a specific reason.
Practice
This program panics. Find the bug from the panic message and fix
it so it prints the sum 60.
package main
import "fmt"
func main() {
nums := []int{10, 20, 30}
total := 0
for i := 0; i <= len(nums); i++ {
total += nums[i]
}
fmt.Println(total)
}
Show solution
The panic says index out of range [3] with length 3: the loop
runs while i <= len(nums), so it tries nums[3], one past the
last valid index. Use < — or, better, range:
package main
import "fmt"
func main() {
nums := []int{10, 20, 30}
total := 0
for _, n := range nums {
total += n
}
fmt.Println(total) // 60
}