Skip to content
Theory · Errors · lesson 2 of 11

errors.New and fmt.Errorf

In the previous lesson an error was a delivery note that arrives with the parcel. Declaring a separate type with an Error() method for every such note is a lot of code for one line. For the usual case, the standard library has two ready-made functions. Let's go through each.

Two functions for errors

Both functions return a value of type error, and it is returned from a function like any value. They differ in what the text can be.

errors.New

The errors.New function from the errors package creates an error with the given text. It takes exactly one string:

GO
package main

import (
    "errors"
    "fmt"
)

func main() {
    err := errors.New("age is negative")
    fmt.Println(err)        // age is negative
    fmt.Println(err == nil) // false
}

Let's walk through the example line by line:

  1. errors.New("age is negative") creates an error and stores it in err.
  2. The first Println prints the error's text: age is negative.
  3. The second prints false: the new error is not equal to nil.

errors.New is like a ready-made "item damaged" stamp: the text is the same with every imprint. You use it when the text is always the same. Its limit is that the text is fixed once and for all: errors.New cannot insert a number into it.

Here the stamp picture is inaccurate. Two imprints of one stamp read as the same remark. But two calls to errors.New("age is negative") give two different errors: their texts match, yet comparing them with == gives false.

fmt.Errorf

When the message needs a value in it, write fmt.Errorf. This is a handwritten remark: "box 3 of 5 is dented". It formats the string by the same rules as fmt.Sprintf, but returns an error instead of a string. The verbs are the same as in Printf:

  • %d—for a number;
  • %q—for a quoted string;
  • %v—for any value, another error included.

The checkAge function uses both functions:

GO
func checkAge(n int) error {
    if n < 0 {
        return errors.New("age is negative")
    }
    if n > 150 {
        return fmt.Errorf("age %d is too big", n)
    }
    return nil
}
GO
fmt.Println(checkAge(200)) // age 200 is too big
fmt.Println(checkAge(30))  // <nil>

Let's go through checkAge:

  1. For a negative age it returns an error with a fixed text through errors.New.
  2. For an age above 150 the text needs the number itself, so here it is fmt.Errorf, and checkAge(200) prints age 200 is too big.
  3. For a valid age the function returns nil, and fmt.Println prints <nil>.

The %w and %v verbs

Another error can go into fmt.Errorf through two verbs, and they give different results.

The %w verb

The %w verb wraps an existing error: the new error keeps the old one inside, and the errors.Is function finds it there later. It is like pinning the old note to a new one: the text is new, and the original remark stays attached.

The %v verb

The %v verb inserts only the text and does not keep the error itself. It retells the old remark in its own words, and the original error is no longer inside the new one.

Which function to choose

The choice between the two functions depends only on the text:

  • the text is always the same—errors.New is enough;
  • the text contains a number, a name or another error—fmt.Errorf is needed.

How to write the text

Error messages in Go follow a few rules:

  • the text starts with a lowercase letter;
  • there is no period at the end;
  • the message names what failed and with which value: age 200 is too big, not bad input;
  • the text does not say error or failed: that it is an error is already clear, since the value came in the variable err.

The first two rules exist because messages get joined. The calling code adds its own context before the text, separated by a colon. fmt.Errorf("load user: %v", err) gives load user: age is negative, and the line reads as one phrase. A capital letter and a period would end up in its middle.

Common mistake

A number was put into the text through errors.New:

GO
err := errors.New("age %d is too big", 200) // ✗
TEXT
too many arguments in call to errors.New

The errors.New function takes exactly one string and formats nothing: a stamp cannot fill in numbers. Text with values needs fmt.Errorf("age %d is too big", n).

Key points

  • errors.New creates an error with a fixed text.
  • fmt.Errorf inserts values into the text by the Printf rules and returns an error.
  • %w in fmt.Errorf keeps the original error inside the new one, while %v keeps only its text.
  • An error message starts with a lowercase letter and does not end with a period.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way