Open-source project
pkg/errors avatar
pkg/errors

pkg/errors: Wrap builds a chain, Cause walks it by interface

Simple error handling primitives

8,256 stars712 forksGoBSD-2-Clause

At a glance

What is it?
pkg/errors is a small Go library that adds context to an error without destroying the original value, and unwinds it again through a one-method interface. Its own roadmap says the package is in maintenance mode because of the Go2 error proposals, and the newest tag is still v0.9.1 from 2020.
Who is it for?
Adopt pkg/errors in an existing codebase that already wraps errors with it, since the value is in the chain your callers already type-switch on. Do not start a new project on it: the README declares maintenance mode, declines new functionality, and its roadmap ends with a 1.0 final release that has not been tagged.
Can I use it commercially?
Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • pkg/errors8,256 ★ · +2
Star history
Repository2026-09-222026-11-13
pkg/errors8.3k8.3k

2 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The failure the package exists to fix is recursive return err

The problem statement in the README is the Go idiom itself, applied up the call stack:

go
if err != nil {
        return err
}

Recursively, that produces error reports with no context and nothing to debug with, because the function that actually failed is nowhere in the value. The library adds context to the failure path without destroying the original value of the error, and the whole thing is one import away:

bash
go get github.com/pkg/errors

The mechanism is a function rather than a type. `errors.Wrap` returns a new error carrying the message you supply and holding on to the one you passed:

go
_, err := ioutil.ReadAll(r)
if err != nil {
        return errors.Wrap(err, "read failed")
}

That example also shows the age of the code: `ioutil.ReadAll` is the pre-Go-1.16 spelling, and the repository still uses it in its own documentation.

Cause unwinds by asking whether an error implements causer

Retrieving the original error is done with an interface, not a field. The contract is one method:

go
type causer interface {
        Cause() error
}

Any error value implementing that interface can be inspected by `errors.Cause`, which recursively retrieves the topmost error that does not implement `causer` and treats it as the original cause. The type switch at the call site is the payoff:

go
switch err := errors.Cause(err).(type) {
case *MyError:
        // handle specifically
default:
        // unknown error
}

Here is the boundary worth knowing before you rely on it. The walk stops at the first error that does not implement `causer`, full stop. If your chain contains a wrapped error from elsewhere that exposes a different unwrap method, `Cause` hands you that value as the cause rather than continuing past it, and the type switch lands in its default branch. Detection therefore depends on every link in the chain agreeing on this one interface.

The roadmap promised 1.0 and the newest tag is v0.9.1

The README states the reason for the current posture in one sentence: with the upcoming Go2 error proposals, this package is moving into maintenance mode. The roadmap it then lists has two items. Version 0.9 was to remove pre-Go 1.9 and Go 1.10 support and address outstanding pull requests if possible; version 1.0 was to be the final release.

v0.9.0 was published on 2020-01-12 and v0.9.1 on 2020-01-14, two days later, and there is no 1.0 in the release record. So the plan was to end the project at a version that has not been cut.

The contributing section is consistent with that: because of the Go2 errors changes, the package is not accepting proposals for new functionality, while pull requests, bug fixes and issue reports are still welcome, with the expectation that you raise an issue before sending a change. For an adopter, that is a clear policy rather than an ambiguous signal. Features will not arrive; correctness fixes might.

make check downloads its linters with go get

The Makefile treats the check target as the whole quality gate: `check` runs test, vet, gofmt, misspell, unconvert, staticcheck, ineffassign and unparam in that order, and `pedantic` adds errcheck on top. Each tool is fetched at run time rather than pinned in a module file, for example:

bash
$(GO) get honnef.co/go/tools/cmd/staticcheck
staticcheck -checks all $(PKGS)

`$(PKGS)` is `github.com/pkg/errors`, and the same variable feeds the test and vet targets; `SRCDIRS` is resolved from it through `go list`. The spell checker is configured with a locale, `misspell -locale GB -error *.md *.go`. Formatting is checked with `gofmt -s -l -d -e`, and the target fails when that pipeline prints anything.

Two consequences for a contributor: running `make check` needs network access on first use because the tools are installed on demand, and `go get` as an installer is a pattern the toolchain has since moved away from, so a toolchain new enough to have dropped that behaviour will have something to fix here.

The CI badges still point at travis and appveyor

The badge row at the top of the README links to travis-ci.org for the `pkg/errors` project and to an AppVeyor project for `davecheney/errors` on branch master, plus godoc, goreportcard and a sourcegraph badge. The same two services appear in the repository as `.travis.yml` and `appveyor.yml`.

Both are visible in the file layout, which also holds a Makefile, LICENSE, `.github/` and `.gitignore`. So continuous integration for this package is described by two configuration files for services that predate the GitHub Actions workflow the `.github/` directory would otherwise suggest, and the README's badges are the only visible signal of build status for anyone reading the page without cloning.

For someone deciding whether to depend on the package, that matters less than it would for an active library, because the README has already answered the maintenance question in the maintenance mode sentence. What the badges cannot tell you is the state of the build today.

Three source files, and test names that pin the output format

The library itself is small: `errors.go` and `stack.go` are the implementation, and `go113.go` sits beside them with its own `go113_test.go`, so the package is version-aware at the source level even though the README says nothing about what that file contains.

The test files are named for what they cover: `stack_test.go`, `format_test.go`, `json_test.go`, `example_test.go` and `bench_test.go` alongside `errors_test.go`.

The documentation for the rest sits on godoc.org, linked from the README, and the README itself defers to it for anything beyond Wrap and Cause.

What the package will not do is stated in its own README

Set an expectation before you adopt it. This is not a general error handling framework: the primitives are adding context and recovering the original value, and the contributing section says the package is not accepting proposals for new functionality. If you need structured error types with codes, error grouping or anything that inspects an error chain field by field, this is not the library.

The second limit is interop. `errors.Cause` is the only unwrapping tool here, and it recognises one interface. A chain assembled from several libraries, some of which wrap errors differently, gives `Cause` a point where it stops and returns something the type switch in your code does not recognise. That failure is silent: you get your default branch rather than an error.

What remains is what the package has always been good at, adding a sentence to a failure path so the log says which step failed. Everything after that sentence is either in godoc.org or in a proposal the Go project was writing while this README was being updated.

Editorial conclusion

Adopt pkg/errors in an existing codebase that already wraps errors with it, since the value is in the chain your callers already type-switch on. Do not start a new project on it: the README declares maintenance mode, declines new functionality, and its roadmap ends with a 1.0 final release that has not been tagged. Before you import it anywhere new, check whether your Go version already has the wrapping helpers the Go2 proposals led to, and read the causer interface rather than assuming Cause unwraps every chain it meets.

Frequently asked questions

What does pkg/errors do in Go?

It provides simple error handling primitives for adding context to a failure path without destroying the original error value, which the standard if err != nil idiom loses as it returns up the call stack. `errors.Wrap` adds context and `errors.Cause` retrieves the original.

How do I add context to an error with pkg/errors?

Wrap the error at the point where you know what failed. The README's example reads a reader with `ioutil.ReadAll` and returns `errors.Wrap(err, "read failed")` when it fails, which adds the message while keeping the original value reachable.

How does errors.Cause find the original error?

It recursively retrieves the topmost error that does not implement the one-method `causer` interface, defined in the README as `type causer interface { Cause() error }`. You then type switch on the result, for example against your own `*MyError` type.

Is pkg/errors still getting updates, and will there be a 1.0 release?

The README says the package is moving into maintenance mode because of the upcoming Go2 error proposals and that it is not accepting proposals for new functionality, while bug fixes, pull requests and issue reports are still welcome. The roadmap listed 0.9 and then a final 1.0 release; v0.9.1 is the newest tag, published on 2020-01-14, and the last push was on 2026-03-27.

Official sources

  1. License: BSD-2-Clause
  2. pkg/errors on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pkg-errors.svg)](https://hysenlabs.com/projects/pkg-errors)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/pkg-errors.svg)](https://hysenlabs.com/projects/pkg-errors)

Was this useful?