SoFunction
Updated on 2025-03-04

Detailed explanation of the tools and techniques for handling errors in go language

introduction

I've seen a lotgolangThe developers of the community, especially those attracted by its simplicity,golangThere are some quick judgments on how things should be handled.

One of the things is error handling. Since most language developers are fromOOPBackground, they are used to handling exceptions, or the concept of "throwing" exceptions to stop the flow of the current application, and they mostly think that this is alsogolangThe way we have to stop the flow of our application in case of an error. They were wrong!

Don't abuse your tools

I've seen a lot, and I did this before. Whenever an unexpected situation occurs, use it(1), and then move on. Well, this is not the right way to use go!

I understand why this is widely used, because most of the early onesgolangApplications are just terminal tools, and many of these tools have been used.shTo build the executable file, we used to just exit 1; to indicate that something unexpected happened just now and we wanted to exit.

We brought this habit to our golang simple terminal application and then to complex applications, which is just another Cargo Cult Programming. I highly encourage you to be very careful when you have to do this, because it is:

  • As your application grows, it is very difficult to maintain.
  • Most importantly, it is impossible to unit test such a code, which clearly shows its uncleanness.
  • Exiting in this way will prevent the execution of any delayed operations from being executed and your program will be terminated immediately, which may lead to resource leakage. Please consider this example:
func main() {
    dbConnection := ("...")
    defer () // this operation won't be executed!    
    entity := Entity{}
    err := (entity)
    if err != nil {
        (1)
    }
}

Consider passing your error

The error is justgolangAnother type in which you must use to control the execution flow of your program.

In order to do this, we have to propagate these errors throughout the program until the appropriate processing point.

Consider an HTTP API that manages orders, we want to prohibit customers from placing orders under specific conditions, such as:

package order
// package errors
var (
    UnableToShipToCountry = ("unable to ship order to the requested country")
)
type Order struct {
    // ... order fields
}
type OrderRepo struct {
    DB db
    // ...
}
func newOrderFromRequest(o OrderRequest) (Order, error) {
    if  != "DE" {
	return UnableToShipToCountry
    }    // ... the creation logic
    return Order{...}, nil
}
func (r *OrderRepo)PlaceOrder(o OrderRequest) error {
    order, err := newOrderFromRequest(o)
    if err != nil {
        // don't handle the error here, its handling may differ
        return err
    }    // ... db transaction may also return an error
    return (order)
}

In ourhttp packagemiddle:

package http
("/order", func (w , r *) {
    orderRequest := createOrderRequest(r)
    err := (orderRequest)
    if (err, ) {
        ()
        return
    }
    if err != nil {
        // this error in case of DB transaction failure
        ()
        return
    }    // ...
    ()
})

Customize your errors

We can create our own custom error value and use it in our program while considering adding some useful information like error tracking which may add a beneficial value to our logs, especially during debugging.

type AppErr struct {  
    msg string  
    code int  
    trace string  
}  
func (e AppErr) Error() string {  
    return ("Msg: %s, code: %d, trace:\n %s", , , )  
}  
func NewAppErr(msg string, code int) AppErr {  
    stackSlice := make([]byte, 512)  
    s := (stackSlice, false)  
    return AppErr{msg, code, ("\n%s", stackSlice[0:s])}  
}

And we have such a use case in a package:

package admin  
func A() error {  
    return b()  
}  
func b() error {  
    return NewAppErr("error from b function!", 3)  
}

:

func main() {  
    err := ()
    (err)
}

The logged error message will be:

Msg: error from b function!, code: 3, trace:  
goroutine 1 [running]:  
./cmd/app/({0x1f42b0, 0x17}, 0x7)  
./cmd/app/error/:16 +0x35  
./cmd/app/(...)  
./cmd/app/admin/**:12**  
./cmd/app/(...)  
./cmd/app/admin/**:8**  
()  
./cmd/app/**:10** +0x8d

You can also consider turning off your tracking print in production, or checking for other configuration values.

The above is the detailed explanation of the Go language's elegant processing error tools and techniques. For more information about Go language processing error tools, please pay attention to my other related articles!