Errors as values

Contents
Return errors
Check them immediately
Wrap with %w
Custom error types

Return errors

Go has no exceptions: a function that can fail returns its result together with an error value. Success carries nil, and failure carries a value explaining what went wrong.

package main import ( "errors" "fmt" ) func divide(a float64, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } func main() { fmt.Println(divide(7, 2)) fmt.Println(divide(7, 0)) }

go run main.go

3.5 <nil>
0 division by zero

Errors are ordinary values, so they travel through variables, struct fields, and return statements like any other data. The errors.New helper builds a simple error from a message string.

Check them immediately

Handle the error before touching the result. The standard shape is one if statement right after the call: on failure report the problem or return early, otherwise continue with the value.

package main import ( "errors" "fmt" ) func divide(a float64, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } func main() { result, err := divide(7, 0) if err != nil { fmt.Println("oops:", err) } else { fmt.Println(result) } }

oops: division by zero

Linters flag unchecked errors, and reviewers will too: every function that returns an error expects the caller to look at it. Returning the error upward is fine when the current level has nothing useful to add.

Wrap with %w

Add context while keeping the original error reachable by wrapping it with fmt.Errorf and the %w verb. Callers can then match the root cause with errors.Is even through several layers of context.

package main import ( "errors" "fmt" ) var errEmpty = errors.New("empty name") func greet(name string) error { if name == "" { return fmt.Errorf("greet failed: %w", errEmpty) } fmt.Println("Hello,", name) return nil } func main() { err := greet("") fmt.Println(err) fmt.Println(errors.Is(err, errEmpty)) }

greet failed: empty name
true

Use %w for the cause and %v or %s for plain details: only %w links the chain for errors.Is and errors.As. One wrap per level is enough, at the point where fresh context appears.

Custom error types

Anything that implements the error interface with an Error method returning a string counts as an error. Custom types let failures carry structured data such as status codes instead of plain text.

package main import "fmt" type HTTPError struct { Code int Msg string } func (e HTTPError) Error() string { return fmt.Sprintf("http %d: %s", e.Code, e.Msg) } func fetch(code int) error { if code != 200 { return HTTPError{Code: code, Msg: "bad response"} } return nil } func main() { fmt.Println(fetch(200)) fmt.Println(fetch(404)) }

<nil>
http 404: bad response

Keep custom errors in the package that produces them so callers can match them with errors.As and read their fields. Start with errors.New and grow into custom types only when callers need more than a message.

Next: Pointers

Article author: Arthur Isaev

Related articles
Golang
Go course: zero to hero in 50 lessons
What is Go and where it runs
Install Go and check the version
Your first Go program
Modules with go mod
Variables
Constants
Types
Numbers
Strings
Conditions with if
Switch in Go
Loops with for
Arrays in Go
Slices in Go
Maps in Go
Functions in Go
Errors as values
Pointers in Go
Structs in Go
Methods in Go
Interfaces in Go
Embedding instead of inheritance
Generics in Go
Packages and imports
The go toolchain
Testing with go test
Advanced errors
defer in Go
Strings, bytes and runes
Time in Go
JSON
Files and IO
HTTP servers
HTTP clients
Context
Goroutines
Channels
select and sync
CLI: args, flags, env
Logging
Regular expressions
Sorting
Concurrency patterns
Databases with database/sql
Benchmarks and profiling
Toolchain and CI
Project layout
Capstone: wordfreq CLI
Capstone: JSON API server
Hero roadmap: the whole course on one page
Install Go on Windows 11
Install Go on Ubuntu
Install Go on Rocky Linux
Install Go on macOS
Install Go on FreeBSD
Go in Docker

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.ru
Write to info@urn.su if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: