Golang return error: create, wrap, and check errors in Go

Tech reviewed: Deepak Prasad
Golang return error: create, wrap, and check errors in Go

People search golang return error, go return error, return error golang, and how to return error in golang because Go treats failures as ordinary return values, not exceptions. This article shows golang return new error patterns with errors.New and fmt.Errorf, the usual golang if err check, wrapping with %w, and errors.Is / errors.As. If you are looking for golang throw error or golang throw, Go does not throw for expected failures; use returned errors instead, and reserve panic for rare cases covered in panic and recover.

Examples were run with a recent Go toolchain on Linux. Snippets are self-contained so you can run them without passing command-line arguments.


Golang return error: the error interface and nil

In Go, error is a small interface: any type with an Error() string method satisfies it. Library functions often return (T, error); the zero value for the interface side is nil, which means “no error.”

go
package main

import (
	"errors"
	"fmt"
)

func greet(name string) (string, error) {
	if name == "" {
		return "", errors.New("name is empty")
	}
	return "Hello, " + name, nil
}

func main() {
	msg, err := greet("Ada")
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println(msg)

	if _, err := greet(""); err != nil {
		fmt.Println("error:", err)
	}
}
Output

Run prints a greeting line, then an error line for the empty name.


Golang return new error: errors.New and fmt.Errorf

errors.New builds a simple error from a string. fmt.Errorf formats text and, with %w, wraps another error so errors.Is and errors.Unwrap can see the chain.

go
package main

import (
	"errors"
	"fmt"
)

var ErrNotFound = errors.New("not found")

func lookup(id int) error {
	if id < 0 {
		return fmt.Errorf("lookup %d: %w", id, ErrNotFound)
	}
	return nil
}

func main() {
	err := lookup(-1)
	fmt.Println(err)
	fmt.Println(errors.Is(err, ErrNotFound))
}
Output

Run prints the wrapped message and true for errors.Is.


Golang if err: call sites and early return

The idiomatic golang if err pattern is if err != nil { return ..., err } (or handle locally). Multiple steps chain cleanly with early returns.

go
package main

import (
	"errors"
	"fmt"
)

func stepA() error {
	return errors.New("step A failed")
}

func pipeline() error {
	if err := stepA(); err != nil {
		return fmt.Errorf("pipeline: %w", err)
	}
	return nil
}

func main() {
	if err := pipeline(); err != nil {
		fmt.Println(err)
	}
}
Output

Run prints one wrapped error line.


errors.Is, errors.As, and wrapping with %w

Use errors.Is(err, target) to detect a sentinel anywhere in the unwrap chain. Use errors.As(err, &ptr) to extract a concrete error type.

go
package main

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
)

func readName(path string) error {
	_, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			return fmt.Errorf("read %q: %w", path, fs.ErrNotExist)
		}
		return fmt.Errorf("read %q: %w", path, err)
	}
	return nil
}

func main() {
	err := readName("/no/such/file/for/golang-return-error-demo")
	fmt.Println(err)
	fmt.Println(errors.Is(err, fs.ErrNotExist))
}

Run shows a wrapped message and true for fs.ErrNotExist.


Golang error to string

The standard way to turn an error into a string is err.Error(). fmt verbs such as %v also use the error string when printing.

go
package main

import (
	"errors"
	"fmt"
)

func main() {
	err := errors.New("something went wrong")
	fmt.Println(err.Error())
	fmt.Printf("%v\n", err)
}
Output

Run prints the same message twice.


Panic versus returned errors

For golang throw style control flow in other languages, Go instead returns an error. panic stops the current goroutine unless recovered; it is not a substitute for validation errors. See golang panic for defer and recover when you must hard-abort a path.


Summary

Golang return error means returning a non-nil error (often last in the result list) and letting callers run golang if err checks. golang return new error values come from errors.New or fmt.Errorf, and %w keeps underlying causes visible to errors.Is and errors.As. golang error to string is err.Error() or normal printing with %v. go return error and return error go are the same model: no exceptions for expected failures, and golang throw error is a misnomer compared to languages with try/catch. For structuring functions that return errors, see functions in Go.


References


Frequently Asked Questions

1. Is it fmt.Error or fmt.Errorf?

Use fmt.Errorf to build an error value with formatting. There is no fmt.Error function for that purpose; the article examples use fmt.Errorf.

2. What is the difference between errors.New and fmt.Errorf?

errors.New takes a fixed string. fmt.Errorf builds a formatted message and can wrap another error with the %w verb.

3. When should I use errors.Is versus errors.As?

Use errors.Is to compare an error (including a wrapped chain) to a sentinel value. Use errors.As to test whether some error in the chain can be assigned to a typed pointer you provide, such as *os.PathError.

4. Does Go use try and catch?

No. Normal failures are explicit error returns; defer is still useful for cleanup alongside returned errors.

5. How do I get the underlying error from a wrapped one?

Use errors.Unwrap, or rely on errors.Is and errors.As which walk the chain created by %w wrapping.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, …

  • Red Hat Certified System Administrator in Red Hat OpenStack
  • Certified Kubernetes Application Developer (CKAD)
  • Red Hat Certified Specialist in Ansible Automation
  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security