09 - Errors

📋 Jump to Takeaways

Things go wrong. A file doesn't exist. A user types "abc" when you expected a number. A config file is corrupted. In many languages, these problems crash your program with an exception. Go does it differently. Errors are just values. Functions that can fail return an error alongside their result, and you decide what to do with it.

The error Type

An error in Go is a value of type error. Functions that can fail return it as a second value:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	n, err := strconv.Atoi("42")
	fmt.Println(n, err) // 42 <nil>

	n, err = strconv.Atoi("abc")
	fmt.Println(n, err) // 0 strconv.Atoi: parsing "abc": invalid syntax
}

strconv.Atoi converts a string to an integer. If the conversion works, err is nil (Go's word for "nothing"). If it fails, err contains a message explaining what went wrong.

Notice the import block at the top. strconv is a package from Go's standard library. We'll cover imports and packages fully in the next lesson. For now, just know that you import a package to use its functions.

Checking Errors

The standard pattern is to check err immediately after the call:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	n, err := strconv.Atoi("abc")
	if err != nil {
		fmt.Println("That's not a number:", err)
		return
	}
	fmt.Println("Got:", n)
}

if err != nil means "if something went wrong." This pattern appears everywhere in Go.

Returning Errors from Your Functions

import "errors"

func divide(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("cannot divide by zero")
	}
	return a / b, nil
}

Return nil when everything is fine. Return an error when something goes wrong.

Try it yourself: add package main, a func main() that calls divide, and handle the error. Run it in the Go Playground or locally.

fmt.Errorf

Include dynamic information in an error message:

func withdraw(balance, amount int) (int, error) {
	if amount > balance {
		return balance, fmt.Errorf("cannot withdraw %d, only %d available", amount, balance)
	}
	return balance - amount, nil
}

Reading and Writing Files

// Read
data, err := os.ReadFile("notes.txt")
if err != nil {
	fmt.Println("Could not read file:", err)
	return
}
fmt.Println(string(data))

// Write
err = os.WriteFile("output.txt", []byte("Hello!"), 0644)
if err != nil {
	fmt.Println("Could not write file:", err)
	return
}

os.ReadFile returns bytes and an error. string(data) converts bytes to text. os.WriteFile takes a filename, bytes, and a permission number (0644 = owner read/write, others read-only).

For complete running examples of file reading and writing, check the "Ready to Run" section at the bottom of this lesson.

Ignoring Errors (Don't)

You can ignore an error with _:

n, _ := strconv.Atoi("abc")
fmt.Println(n) // 0

This compiles, but n is silently 0 and you have no idea something went wrong. Handle errors.

Key Takeaways

  • Errors in Go are values of type error, not exceptions
  • Functions that can fail return (result, error). Check err != nil immediately
  • errors.New("message") creates a simple error
  • fmt.Errorf("message %s", val) creates an error with formatted text
  • nil means no error. Return nil when everything is fine
  • os.ReadFile and os.WriteFile read and write files, returning errors
  • strconv.Atoi converts strings to integers, returning an error for bad input
  • Don't ignore errors with _ unless you truly don't care about failure

🚀 Ready to run?

Complete runnable examples for this lesson.

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

© 2026 ByteLearn.dev. Free courses for developers. · Privacy