Over the past decade, we have successfully exploited the fact that Go handles 
errors as values . Although the standard library had minimal support for errors: only the 
errors.New and 
fmt.Errorf functions that generate an error containing only a message - the built-in interface allows Go-programmers to add any information. All you need is a type that implements the 
Error method:
 type QueryError struct { Query string Err error } func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() } 
These types of errors are found in all languages and store a wide variety of information, from timestamps to file names and server addresses. Low-level errors that provide additional context are often mentioned.
A pattern where one error contains another is so common in Go that after a 
heated discussion in Go 1.13 its explicit support was added. In this article, we will look at additions to the standard library that provide the mentioned support: three new functions in the errors package and a new formatting command for 
fmt.Errorf .
Before discussing the changes in detail, let's talk about how errors were investigated and constructed in previous versions of the language.
Errors before Go 1.13
Error research
Errors in Go are meanings. Programs make decisions based on these values in different ways. Most often, the error is compared to nil to see if the operation failed.
 if err != nil {  
Sometimes we compare the error to find out the 
control value and to understand if a specific error has occurred.
 var ErrNotFound = errors.New("not found") if err == ErrNotFound {  
The error value can be of any type that satisfies the error interface defined in the language. A program can use a type statement or a type switch to view the error value of a more specific type.
 type NotFoundError struct { Name string } func (e *NotFoundError) Error() string { return e.Name + ": not found" } if e, ok := err.(*NotFoundError); ok {  
Adding Information
Often a function passes an error up the call stack, adding information to it, for example, a short description of what happened when the error occurred. This is easy to do, just construct a new error that includes the text from the previous error:
 if err != nil { return fmt.Errorf("decompress %v: %v", name, err) } 
When creating a new error using 
fmt.Errorf we discard everything except the text from the original error. As we saw in the 
QueryError example, sometimes you need to define a new type of error that contains the original error in order to save it for analysis using code:
 type QueryError struct { Query string Err error } 
Programs can look inside the 
*QueryError and make a decision based on the original error. This is sometimes called the “unwrapping” of an error.
 if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {  
The 
os.PathError type from the standard library is another example of how one error contains another.
Errors in Go 1.13
Unwrap Method
In Go 1.13, the 
errors and 
fmt standard library packages simplified 
fmt errors that contain other errors. The most important is the convention, not the change: an error containing another error can implement the 
Unwrap method, which returns the original error. If 
e1.Unwrap() returns 
e2 , then we say that 
e1 packs e2 and you can 
unpack e1 to get 
e2 .
According to this convention, you can give the 
QueryError type described above to the 
QueryError method, which returns the error it contains:
 func (e *QueryError) Unwrap() error { return e.Err } 
The result of unpacking the error may also contain the 
Unwrap method. The sequence of errors obtained by repeated unpacking, we call the 
chain of errors .
Investigating Errors Using Is and As
In Go 1.13, the 
errors package contains two new functions for investigating errors: 
Is and 
As .
The 
errors.Is function compares an error with a value.
 
The 
As function checks to see if the error is of a particular type.
 
In the simplest case, the 
errors.Is function behaves like a comparison with a control error, and the 
errors.As function behaves like a type statement. However, when working with packed errors, these functions evaluate all errors in the chain. Let's look at the above 
QueryError example to examine the original error:
 if e, ok := err.(*QueryError); ok && e.Err == ErrPermission {  
Using the 
errors.Is function, 
errors.Is can write this:
 if errors.Is(err, ErrPermission) {  
The 
errors package also contains a new 
Unwrap function that returns the result of calling the 
Unwrap method of the error, or returns nil if the error does not have the 
Unwrap method. It is usually better to use 
errors.Is or 
errors.As , since they allow you to examine the entire chain with a single call.
Error packaging with% w
As I mentioned, it is normal practice to use the 
fmt.Errorf function to add additional information to the error.
 if err != nil { return fmt.Errorf("decompress %v: %v", name, err) } 
In Go 1.13, the 
fmt.Errorf function supports the new 
%w command. If it is, then the error returned by 
fmt.Errorf will contain the 
Unwrap method that returns the argument 
%w , which should be an error. In all other cases, 
%w identical to 
%v .
 if err != nil {  
Packing the error with 
%w makes it available for 
errors.Is and 
errors.As :
 err := fmt.Errorf("access denied: %w", ErrPermission) ... if errors.Is(err, ErrPermission) ... 
When to pack?
When you add an additional context to the error using 
fmt.Errorf or a custom type implementation, you need to decide whether the new error will contain the original. There is no single answer to this, it all depends on the context in which the new error is created. Pack to show her caller. Do not package the error if this leads to disclosure of implementation details.
For example, imagine a 
Parse function that reads a complex data structure from 
io.Reader . If an error occurs, we will want to find out the number of the row and column where it occurred. If an error occurred while reading from 
io.Reader , we will need to pack it to find out the reason. Since the caller was provided with the 
io.Reader function, it makes sense to show the error that it generated.
Another case: a function that makes several database calls probably should not return an error in which the result of one of these calls is packed. If the database used by this function is part of the implementation, then disclosing these errors will violate the abstraction. For example, if the 
LookupUser function from the 
pkg package uses the Go 
database/sql package, then it may encounter the 
sql.ErrNoRows error. If you return an error using 
fmt.Errorf("accessing DB: %v", err) , then the caller cannot look inside and find 
sql.ErrNoRows . But if the function returns 
fmt.Errorf("accessing DB: %w", err) , then the caller could write:
 err := pkg.LookupUser(...) if errors.Is(err, sql.ErrNoRows) … 
In this case, the function should always return 
sql.ErrNoRows if you do not want to break clients, even when switching to a package with a different database. In other words, packaging makes an error part of your API. If you do not want to commit support for this error in the future as part of the API, do not package it.
It is important to remember that regardless of whether you pack it or not, the error will remain unchanged. 
A person who will understand it will have the same information. Making decisions about packaging depends on whether additional information is needed for 
programs so that they can make more informed decisions; or if you want to hide this information in order to maintain the level of abstraction.
Setting Up Error Testing Using Is and As Methods
The 
errors.Is function checks every error in the chain against the target value. By default, an error matches this value if they are equivalent. In addition, an error in the chain can declare its compliance with the target value using the implementation of 
the Is method .
Consider the error caused 
by the Upspin package , which compares the error with the template and evaluates only nonzero fields:
 type Error struct { Path string User string } func (e *Error) Is(target error) bool { t, ok := target.(*Error) if !ok { return false } return (e.Path == t.Path || t.Path == "") && (e.User == t.User || t.User == "") } if errors.Is(err, &Error{User: "someuser"}) {  
The 
errors.As function also advises the 
As method, if any.
Errors and Package APIs
A package that returns errors (and most packages do this) should describe the properties of these errors that the programmer can rely on. A well-designed package will also avoid returning errors with properties that cannot be relied upon.
The simplest thing is to say whether the operation was successful, returning, respectively, the value nil or non-nil. In many cases, other information is not required.
If you need the function to return an identifiable error state, for example, “element not found”, then you can return an error in which the signal value is packed.
 var ErrNotFound = errors.New("not found")  
There are other patterns for providing errors that the caller can semantically examine. For example, directly return a control value, a specific type, or a value that can be analyzed using a predicative function.
In any case, do not disclose the internal details to the user. As mentioned in the chapter “When is it worth packaging?”, If you return an error from another package, then convert it so as not to reveal the original error, unless you intend to commit yourself to return this specific error in the future.
 f, err := os.Open(filename) if err != nil {  
If a function returns an error with a packed signal value or type, then do not directly return the original error.
 var ErrPermission = errors.New("permission denied")  
Conclusion
Although we discussed only three functions and a formatting command, we hope that they will help greatly improve error handling in Go programs. We hope that packaging for the sake of providing additional context will become a normal practice, helping programmers make better decisions and find bugs faster.
As Russ Cox said in his 
speech at GopherCon 2019 , on the way to Go 2 we experiment, simplify and ship. And now, having shipped these changes, we are embarking on new experiments.