This text was initially revealed by a neighborhood contributor, Christoph Berger, within the JetBrains’ Go Information and has since been moved to the JetBrains Go weblog. We now have additionally up to date it in August 2026 to mirror the newest adjustments to the Go language.
Error dealing with is likely one of the elements wherein Go differs from different standard languages like Java, C++, JavaScript, and Python. In Go, errors are values. Whereas different languages transfer error dealing with out of the code move, Go considers errors a pure a part of this system move. If a operate encounters an error, it returns that error alongside different return values. The caller has the responsibility to examine this error and deal with it accordingly.
A typical Go package deal or app can encounter varied varieties of errors at runtime, together with logical errors, I/O errors, community errors, knowledge validation errors, and extra. Every of those sorts could require particular error dealing with. Go gives a set of instruments and methods to deal with various kinds of errors.
This text explores a number of elements of error dealing with in Go. You’ll be taught error dealing with methods and greatest practices, the way to tackle particular varieties of errors, and the way to keep away from widespread errors in error dealing with.
Earlier than you begin
All examples used on this information are inlined, in order that simply studying the snippets must be sufficient to get the image. If, nevertheless, you wish to observe alongside and tinker with the code your self as you go, we now have a repository with code samples from totally different articles revealed on the GoLand weblog. The code for this information resides within the error-handling listing.
You need to use an IDE of your alternative or set up the GoLand IDE. There’s a free trial obtainable; in case you are new to GoLand, it is a nice likelihood to try it out!
Then, fork or clone the repository that incorporates the code for this information.
Comply with these steps to open the code in GoLand:
- Begin GoLand.
- If it’s a contemporary set up, you’ll be prompted with a welcome display screen. Click on the Open button.
- Within the file selector dialog that opens, navigate to the repository you cloned earlier, choose the folder
error-handling, and click on Open.
And also you’re set! Preserve the IDE inside attain whereas following the information.
Widespread error dealing with methods in Go
As talked about, all error dealing with in Go is predicated on the notion of errors as values. An error in Go is a worth like every other worth. An error worth is of the sort error, which is a built-in kind. However what’s this kind? Fortunately, GoLand makes it simple to examine the supply code of Go itself.
Within the Venture pane, scroll right down to the Exterior Libraries part. Develop Go SDK <put in model>, then increase builtin.go (as a result of error is a built-in kind):

If you happen to can not increase builtin.go, choose the three-dot menu within the Venture pane, then Tree Look, and be certain that Present Members is checked:

Scroll down till you see the error kind beneath builtin.go, then click on it. The file builtin.go opens within the editor space and exhibits the error kind:
kind error interface {
Error() string
}
The error kind is an interface with a single operate, Error() string. Utilizing an interface kind right here means that you can simply create customized error sorts by making the customized kind implement the error interface.
So, let’s see how errors could be dealt with.
Returning errors
Generally, if a operate encounters an error, it doesn’t have the mandatory context to correctly deal with the error by itself, so it has to go the error again to its caller.
For instance, see func ReadFile() from the pattern code (readfile.go):
func ReadFile(path string) ([]byte, error) {
if path == "" {
// Create an error with errors.New()
return nil, errors.New("path is empty")
}
f, err := os.Open(path)
if err != nil {
// Wrap the error.
// If the format string makes use of %w to format the error,
// fmt.Errorf() returns an error that has the
// methodology "func Unwrap() error" carried out.
return nil, fmt.Errorf("open failed: %w", err)
}
defer f.Shut()
buf, err := io.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("learn failed: %w", err)
}
return buf, nil
}
ReadFile() checks the obtained path, and if the trail is empty, it creates a brand new error and returns it. The information that ReadFile() was speculated to return doesn’t exist; due to this fact, ReadFile() returns a nil worth:
if path == "" {
return nil, errors.New("path is empty")
}
Conventionally, if a operate returns an error worth, it’s at all times the final (rightmost) worth within the listing of return values:
func ReadFile(path string) ([]byte, error) {
When ReadFile() is named, it returns the contents of the with an error worth that’s nil on success and non-nil on failure. Sometimes, the returned error worth is assigned to a variable named err (see essential.go within the accompanying repository):
_, err := ReadFile("no/file")
if err != nil {
fmt.Println("Error:", err)
}
Right here, the results of calling ReadFile() just isn’t wanted, as this information seems to be into error dealing with particularly. Subsequently, the return worth is assigned to the clean identifier (_).
Now the caller can take a look at if the error is non-nil and deal with the error accordingly.
Panic and recuperate
Go newcomers may miss the attempt...catch mechanism that different languages present. Nevertheless, Go has one thing that fulfills the same goal: panic and recuperate. However beware! Not like attempt...catch, panic and recuperate just isn’t, and shouldn’t be, the usual means of dealing with errors. Panicking is simply acceptable if an error is certainly sudden and there’s no means of dealing with it. In such circumstances, it’s higher to have the applying crash early and restart it. You’ll be taught extra about this in the most effective practices part later.
An instance of an error that ought to not occur is a failed compilation of a daily expression given as a literal string. As a result of the common expression is thought at compile time, the developer ought to have made it a sound expression in order that the compilation can not fail at runtime. To implement this, the regexp package deal has a operate referred to as MustCompile(). The prefix Should signifies that the operate panics if it can not compile the given common expression.
To display this, the file verifypath.go incorporates a operate that can confirm if a given path is legitimate. Nevertheless, the developer entered the common expression incorrectly – a closing parenthesis is lacking:
func isValidPath(p string) bool {
pathRe := regexp.MustCompile(`(invalid common expression`)
return pathRe.MatchString(p)
}
If this operate is named with none precaution, the app crashes immediately:
panic: regexp: Compile(`(invalid common expression`): error parsing regexp: lacking closing ): `(invalid common expression`
goroutine 1 [running]:
regexp.MustCompile({0x1005ca16d, 0x1b})
/choose/homebrew/choose/go/libexec/src/regexp/regexp.go:319 +0xac
essential.isValidPath({0x1005c76af, 0xd})
/Customers/you/dev/JetBrains/jetbrains-go-code-samples/awesomeProject/error-handling/verifypath.go:6 +0x30
essential.essential()
/Customers/you/dev/JetBrains/jetbrains-go-code-samples/awesomeProject/error-handling/essential.go:20 +0xb0
Course of completed with the exit code 2
The stack hint reveals that line 6 of verifypath.go is the supply of the panic.
In sure circumstances, crashing the app won’t be an possibility. Contemplate an HTTP server that should be up and operating with out disruption. If a panic happens when dealing with a request, all different requests ought to proceed being dealt with, if potential. To do that, the web/http package deal makes use of Go’s restoration method.
There are two eventualities for the way it can work described beneath in case of the panicking isValidPath() operate.
It provides a deferred operate name to the caller
The caller of isValidPath() units up a deferred operate name close to the start of the operate physique:
defer func() {
// deferred code ...
}() // <- Do not forget the parens, that is an precise operate name!
Deferred features are mechanically executed each time the containing operate exits, whether or not via a standard return name or triggered by a panic.
Within the deferred operate, it calls recuperate()
The deferred operate can confirm if it was invoked due to a standard return or due to a panic. It solely must name recuperate() and confirm the returned error (see essential.go on the finish of func essential()):
defer func() {
// Is that this func invoked from a panic?
if r := recuperate(); r != nil {
// Sure: recuperate from the panic
fmt.Println("Recovering")
// ...
}
}()
If the error is nil, the deferred operate was invoked due to a standard return, so no restoration is required.
If the deferred operate was triggered by a panic, recuperate() returns the error that prompted the panic. Now the deferred operate can do no matter is required to recuperate from the panic.
Logging errors
If a operate can deal with an error it receives from a referred to as operate, it’d wish to write details about the error to a log file.
Logging an error is simple in Go, because of the log package deal in the usual library and the slog package deal that’s obtainable from Go 1.21 onwards.
Right here’s an instance utilizing the log package deal within the deferred operate from the earlier part:
if r := recuperate(); r != nil {
log.Printf("Recovering from error `%v`n", r)
}
log.Printf() is a drop-in alternative for fmt.Printf() that writes to the usual logger’s output. To format an error kind, use the %v verb that prints a worth in its default format.
A facet observe: If you happen to write code for a library, take into account not logging something. The library shoppers may have totally different opinions about which logger to make use of and what’s printed to stdout or stderr. So, it’s nearly at all times higher to solely return errors and let the library shoppers do the logging they need.
Utilizing error wrapping
An error usually “bubbles up” a name chain of a number of features. In different phrases, a operate receives an error and passes it again to its caller via a return worth. The caller may do the identical, and so forth, till a operate up the decision chain handles or logs the error. Every operate concerned on this “effervescent up” can add invaluable contextual info to the error earlier than handing it again to its caller. Passing errors in a means that preserves that chain is named “error wrapping”. You add context whereas conserving the unique error inside the brand new one, that may be later unwrapped to examine or match the underlying error.
A operate ought to solely go the error on unchanged if it can not add any useful info:
if err != nil {
// Solely try this if no further context could be added!
return err
}
In all different circumstances, it ought to add acceptable contextual info. Nevertheless, merely concatenating a brand new error message with the unique one doesn’t work:
// WRONG!
if err != nil {
return errors.New("open failed:" + err.Error())
}
This is able to solely protect the unique error message, however flatten the error itself right into a plain string. With kind and structured particulars gone, callers may now not unwrap and examine it.
As a substitute, you must use error wrapping. An error could be “wrapped” round one other error utilizing fmt.Errorf() and the particular formatting verb %w. See the ReadFile() operate within the file readfile.go:
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("open failed: %w", err)
}
os.Open() returns an error kind that incorporates further info, as you will note later. Wrapping the error preserves all this extra info.
Unwrapping wrapped errors
An error returned by a operate may include a number of wrapped errors. Printing or logging the obtained error will even embody all error messages from the wrapped errors. Nevertheless, typically that you must know if a specific kind of error is nested someplace contained in the layers of errors.
For instance, let’s see the way to deal with ReadFile()‘s errors in func essential():
_, err := ReadFile("no/file")
log.Println("err = ", err)
// Unwrap the error returned by os.Open()
log.Println("errors.Unwrap(err) = ", errors.Unwrap(err))
This code snippet prints the next:
Studying a single file: err = open failed: open no/file: no such file or listing Studying a single file: errors.Unwrap(err) = open no/file: no such file or listing
Whereas the wrapped error message is open failed: open no/file: no such file or listing, the unwrapped error incorporates solely open no/file: no such file or listing, excluding the open failed: message that was added to the wrapped error.
This fashion, you’ll be able to unwrap one error after one other till you hit the top of the chain.
Testing for particular error sorts
Sometimes, that you must know if any of the errors inside a series of wrapped errors are of a specific kind.
For instance, os.Open returns an error of kind fs.PathError that not solely data the error but in addition the operation and the trail that prompted it. If you’ll find out that the error chain incorporates this error, you may make use of the extra info for troubleshooting.
To realize this, the errors package deal gives three features: Is(), As(), and AsType() launched in Go 1.26.
errors.Is()
Operate func Is(err, goal error) bool returns true if error err is of the identical kind as goal.
Within the case of the ReadFile() operate, you’ll be able to confirm that the returned error is, or wraps, an fs.ErrNotExist error:
_, err := ReadFile("no/file")
log.Println("err is fs.ErrNotExist:", errors.Is(err, fs.ErrNotExist))
This prints:
err is fs.ErrNotExist: true
errors.As()
You’ll additionally wish to entry the trail info. For this, you not solely want to make sure the error wraps an fs.PathError but in addition entry this PathError and all its strategies.
To do that, use the operate func As(err error, goal any) bool. Like Is(), operate As() returns true if err is or wraps an error of the identical kind as goal, and it additionally unwraps that error and assigns it to goal.
This requires defining a variable of kind fs.PathError and passing a pointer to that variable to As():
goal := &fs.PathError{}
if errors.As(err, &goal) {
log.Printf("err as PathError: path is '%s'n", goal.Path)
log.Printf("err as PathError: op is '%s'n", goal.Op)
}
This may log the trail and the operation that failed:
err as PathError: path is 'no/file' err as PathError: op is 'open'
errors.AsType()
Go 1.26 provides AsType(), a generic, type-safe various to As(). Its signature is func AsType[E error](err error) (E, bool).
Relatively than declaring a goal variable up entrance and passing a pointer to it, AsType() takes the error kind you’re in search of as a kind parameter and returns two values: the matching error (of kind E) and a boolean reporting whether or not a match was discovered. This retains the matched error neatly scoped to the if block:
if goal, okay := errors.AsType[*fs.PathError](err); okay {
log.Printf("err as PathError: path is '%s'n", goal.Path)
log.Printf("err as PathError: op is '%s'n", goal.Op)
}
Similar to the As() instance, this logs the trail and the operation that failed:
err as PathError: path is 'no/file' err as PathError: op is 'open'
AsType() has a few benefits over As(). Since you specify the error kind straight within the name, the compiler checks it for you, so errors reminiscent of passing a worth the place a pointer is required are caught at compile time as an alternative of triggering the runtime panic that As() can produce when handed an unsuitable goal. AsType() additionally avoids the reflection that As() depends on internally, which makes it a little bit quicker.
As() just isn’t deprecated, so current code retains working. For brand new code, nevertheless, AsType() is the really useful alternative, and it’s particularly handy when that you must take a look at for a number of error sorts one after one other, since every matched error stays scoped to its personal department:
if pathErr, okay := errors.AsType[*fs.PathError](err); okay {
log.Println("path error at:", pathErr.Path)
} else if linkErr, okay := errors.AsType[*os.LinkError](err); okay {
log.Println("hyperlink error throughout:", linkErr.Op)
}
Becoming a member of errors
Sometimes, errors get wrapped one after the other whereas being returned to the respective caller. Generally, a operate wants to gather a number of errors and wrap them into one.
Take the operate ReadFiles() (observe the plural) from readfiles.go for instance. This operate reads a number of recordsdata and returns all file contents that have been efficiently learn. If a number of recordsdata fail to be learn, ReadFiles() will gather the errors and be part of them into one.
For this, the errors package deal gives the Be part of() operate (launched in Go 1.20). Let’s see how ReadFiles() makes use of the Be part of() operate:
func ReadFiles(paths []string) ([][]byte, error) {
var errs error
var contents [][]byte
if len(paths) == 0 {
// Create a brand new error with fmt.Errorf() (however with out utilizing %w):
return nil, fmt.Errorf("no paths supplied: paths slice is %v", paths)
}
for _, path := vary paths {
content material, err := ReadFile(path)
if err != nil {
errs = errors.Be part of(errs, fmt.Errorf("studying %s failed: %w", path, err))
proceed
}
contents = append(contents, content material)
}
return contents, errs
}
If an error happens contained in the for loop, it doesn’t break the loop. As a substitute, it’s joined to variable errs, and the loop continues, becoming a member of extra data as they happen.
Lastly, ReadFiles() returns each the contents learn efficiently and the joined error messages.
Dealing with joined errors
Now, you may count on that joined errors could be unwrapped like single errors. Sadly, this isn’t the case. A joined error is definitely a slice of errors, []error. The Unwrap() operate, nevertheless, returns a single error. If referred to as on a joined error, Unwrap() returns nil:
_, err = ReadFiles([]string{"no/file/a", "no/file/b", "no/file/c"})
log.Println("joined errors = ", err)
log.Println("errors.Unwrap(err) = ", errors.Unwrap(err))
The second log line prints:
errors.Unwrap(err) = <nil>
Thankfully, there’s a solution to unwrap the slice of joined errors. The joined error kind itself helps you do that by offering an Unwrap() []error methodology that returns the error slice.
To entry this Unwrap() methodology, you solely must type-assert that the error variable implements this methodology. You possibly can then name it safely:
e, okay := err.(interface{ Unwrap() []error })
if okay {
log.Println("e.Unwrap() = ", e.Unwrap())
}
This prints the total set of joined errors:
Studying a number of recordsdata: e.Unwrap() = [reading no/file/a failed: open failed: open no/file/a: no such file or directory reading no/file/b failed: open failed: open no/file/b: no such file or directory reading no/file/c failed: open failed: open no/file/c: no such file or directory]
Context-based error dealing with
The context package deal is standard for controlling timeouts of requests or canceling a number of goroutines upon request. If you happen to use a cancelable context, you’ll be able to examine and deal with the error that prompted the cancellation.
Since Go 1.20, you’ll be able to even ship a customized error message when canceling a context through the use of a WithCancelCause context. The next is a fundamental instance:
mum or dad := context.Background()
ctx, cancel := context.WithCancelCause(mum or dad)
defer cancel(nil) // Set the trigger to Canceled
cancel(fmt.Errorf("myError")) // Set the trigger to myError
fmt.Println(ctx.Err()) // Output: context.Canceled
fmt.Println(context.Trigger(ctx)) // Output: myError
(Setting up goroutines and cancel conditions can get complicated rapidly. Discover a full instance in readfiles_concurrent.go.)
The context operate WithCancelCause() returns a context and a cancel operate that expects an error kind. When calling cancel, a customized error message could be handed as enter. All events which have entry to the context can retrieve the customized error via context.Trigger(ctx).
Greatest practices for error dealing with in Go
With these error dealing with methods in thoughts, let’s flip to some greatest practices when working with errors in Go.
Use the defer operate
A operate can exit at a number of factors, via return statements in addition to panics. At any time when a operate allocates sources, reminiscent of recordsdata, community connections, or goroutines, use a defer() operate to scrub up any open sources at operate exit.
The ReadFile() operate incorporates a deferred name that closes the opened file:
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("open failed: %w", err)
}
defer f.Shut()
Observe that defer f.Shut() comes after the error examine. If os.Open() fails, it returns a nil file and a non-nil error, so there’s nothing to shut. Deferring the shut earlier than the examine would threat calling it on a nil file.
Present specific error info
Nothing is extra irritating than seeing some cryptic error message like ERROR: EPIC FAIL within the log recordsdata with none clue concerning the context wherein the error occurred.
In case you’re questioning: sure, messages like this do happen in the true world. The issue with such a message is that even the builders who should know their code is likely to be unable to inform what prompted a specific prevalence of this message:
“Look, this specific code is named from so many locations, and we actually can not say what precisely prompted this specific error at this level. We don’t have sufficient context within the log file.”
Subsequently, if a operate encounters an error, it mustn’t go the error verbatim up the decision chain. Relatively, if any contextual info is accessible to assist troubleshoot the error, this info must be added to the error by wrapping it in a brand new error. (See the sooner part on utilizing error wrapping.)
Use panic and recuperate solely when mandatory
Go newcomers usually frown upon Go’s verbose error dealing with and wish to save typing by letting a operate panic as an alternative of dealing with an error. On the high stage, the panic is then recovered and dealt with. This method, nevertheless, is unidiomatic Go and has many downsides. In the beginning, including helpful contextual info (see the earlier part) just isn’t potential with this methodology. Furthermore, as a result of a panic unwinds the decision stack exterior the common name/return move, any operate within the name chain between the top-level operate and the panicking operate incorporates no error dealing with code. How can a reader see that any of those features may observe an error? For comparability, Java has the throws key phrase to listing all exceptions a operate could emit. Go doesn’t have such a function. It’s not potential to see if any of the callees of a operate panics. Commonplace Go error dealing with makes the error move clearly seen.
Go treats errors as a standard a part of this system move as a result of they’re precisely that. If an error happens, it must be dealt with or handed to the caller till some operate up the decision chain handles the error or writes it to a log file for troubleshooting.
If you happen to examine a operate, you’ll wish to instantly see which errors it might encounter and the way it passes them up the decision chain.
Calling panic must be reserved for sudden errors that ought to by no means occur. A tough-coded regexp string, as seen within the “Panic and recuperate” part, is one instance. A tough-coded common expression must be completely crafted and verified, and it should not fail at runtime.
There are additionally some classes of errors that can’t be dealt with in any respect, reminiscent of an out-of-memory state of affairs. If the required reminiscence can’t be allotted, the applying has no significant solution to proceed and may panic.
Alternatively, consumer enter at runtime is anticipated to be unreliable. Any error ensuing from consumer enter, invalid or lacking recordsdata, a community timeout, or different predictable sources of failure can and must be dealt with as an error.
Use libraries and packages that observe error dealing with greatest practices
You probably have a alternative between a number of third-party packages that ship equivalent or related performance, select the one which follows greatest practices for error dealing with.
You’ll not do your self any favors in the event you resolve to make use of the package deal with the fanciest API however with brittle error dealing with. Any package deal that suppresses errors slightly than correctly passing them again – or that gives no context for errors – will flip troubleshooting right into a hit-or-miss debugging nightmare.
So, take a peek on the code inside a package deal to see if it incorporates sturdy code with correct error dealing with. This precautionary measure will repay in the long term.
Create customized error sorts wherever appropriate
As a result of error is an interface, you’ll be able to construct customized error sorts with additional performance so long as they implement Error() string. You noticed an instance within the “Testing for particular error sorts” part, the place os.Open returned an fs.PathError.
This error is a struct that implements the strategies Error(), Unwrap(), and Timeout(), and gives the fields Path, Op, and Error to seize detailed error info:
kind PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
func (e *PathError) Unwrap() error { return e.Err }
// Timeout experiences whether or not this error represents a timeout.func (e *PathError) Timeout() bool {
t, okay := e.Err.(interface{ Timeout() bool })
return okay && t.Timeout()
}
In the identical method, you’ll be able to create your individual error sorts. The one necessary methodology to implement is Error(), however in the event you additionally implement the strategy Unwrap(), then the package deal operate errors.Unwrap() will be capable of unwrap your error.
Dealing with particular varieties of errors
Some varieties of errors require particular therapy as a result of their particular nature. These sorts embody community errors, I/O errors, and system errors.
Community errors
Failing community connections want particular therapy. A community error could be brought on by a everlasting failure or by a brief subject. Code that handles a community error wants to differentiate between these two conditions.
Contemplate the duty of opening a brand new TCP connection. This job can fail as a result of the community is briefly down or as a result of the system on the different finish of the connection is restarting or overloaded and can’t settle for new connections for the time being.
In such circumstances, you’ll wish to attempt connecting once more at a later time. The web.Dial() operate, for instance, helps this by returning a particular error kind, web.OpError, that gives a technique named Non permanent() for testing if the error is anticipated to ultimately go away.
With the Non permanent() methodology, you’ll be able to implement a easy retry algorithm just like the one beneath or a extra subtle technique like exponential backoff:
func connectToTCPServer() error {
var err error
var conn web.Conn
for retry := 3; retry > 0; retry-- {
conn, err = web.Dial("tcp", "127.0.0.1:12345")
if err != nil {
// Verify if err is a web.OpError
opErr := &web.OpError{}
if errors.As(err, &opErr) {
log.Println("err is web.OpError:", opErr.Error())
// take a look at if the error is short-term
if opErr.Non permanent() {
log.Printf("Retrying...n")
proceed
}
retry = 0
}
}
}
if err != nil {
return fmt.Errorf("join failed: %w", err)
}
defer conn.Shut()
// ship or obtain knowledge
return nil
}
I/O errors
Recovering from an I/O error that happens after having learn or written massive quantities of information could be pricey. All the info that’s already been processed as much as the purpose the place the error happens may should be learn or written once more.
To permit for a extra environment friendly restoration, most I/O-related features and strategies in the usual library return not solely an error but in addition the variety of bytes that have been efficiently processed. A typical instance is io.Reader‘s Learn() operate:
kind Reader interface {
Learn(p []byte) (n int, err error)
}
An error restoration process may use this info to proceed the I/O operation the place it was interrupted.
Vital observe: The io package deal gives the sentinel error worth io.EOF (that’s outlined as errors.New("EOF")) to sign the profitable (!) finish of studying an enter stream. Each kind that implements the io.Reader interface ought to keep on with the documented semantics of returning an error:
…a Reader returning a non-zero variety of bytes on the finish of the enter stream could return both
err == EOForerr == nil. The following Learn ought to return0, EOF.
Widespread errors to keep away from when dealing with errors in Go
Whereas Go’s error dealing with could appear uncommon at first sight, it’s logical and easy to make use of. Nevertheless, this doesn’t imply which you could’t make errors with error dealing with. Listed below are some errors to keep away from.
Ignoring errors
The most important mistake a developer could make in any programming language is to disregard errors. Not catching errors early simply results in follow-up errors that may be rather more troublesome to trace down in comparison with the unique error if it had been correctly dealt with.
So, the primary rule for avoiding error dealing with errors is to by no means assign a returned error worth to the clean identifier.
Furthermore, be careful for features whose sole return worth is an error worth. Go doesn’t stop you from utterly ignoring a single return worth, however you should utilize a linter to detect an ignored error return worth. (GoLand even highlights unhandled errors proper within the editor, to make it simple to keep away from this type of mistake.)
Enjoyable truth: do you know that fmt.Println() returns an error worth?
Backside line is, don’t do that:
WriteString(w, s)
Do that as an alternative:
n, err := WriteString(w, s) // error dealing with right here, see beneath
Not wrapping errors in further context when propagating
Usually, if not at all times, a operate that receives an error from calling one other operate can add invaluable contextual info to the error.
So, each time you end up scripting this:
n, err := WriteString(w, s)
if err != nil {
return err
}
take a step again and see in the event you can embody contextual info. Generally, you’ll be able to. Even the operate title could be invaluable info as a result of it means that you can monitor the chain of operate calls that result in the error:
n, err := WriteString(w, s)
if err != nil {
return fmt.Errorf("after writing %d characters: %w", n, err)
}
It’s a couple of extra strokes on the keyboard for you now, however it may be an unlimited time-saver in a while.
Overgeneralizing errors
When composing error messages, be as particular as you’ll be able to. Embody all of the contextual info you’ve gotten.
An error message like “database error” can have a truckload of various potential causes. The message “database error” is genuinely pointless and unhelpful.
Add as a lot info to the error message as you’ll be able to. Contemplate creating customized error sorts that may carry further info; see the os.PathError kind for instance.
Utilizing incorrect error sorts
The actual kind of error worth may appear to be a negligible element. In any case, each error implements kind error interface{ Error() string }, so in the long run, errors are nothing however glorified string sorts, proper?
Fallacious. Customized error sorts can include additional info and allow superior error inspection via errors.Is(), errors.As(), and errors.AsType().
So, everytime you ship an error again to a caller, be certain that to make use of the error kind that’s acceptable for the given error context.
Not logging errors
Error messages are indispensable for troubleshooting. Whether or not an app can deal with an error or whether or not an error forces the app to terminate, the app ought to log that error for postmortem evaluation.
Basically, if a operate observes an error, it ought to both deal with the error or return it to its caller.
If it may deal with the error or if it can not return the error for some purpose (perhaps as a result of it’s operate essential()), the operate ought to at all times log the error and all its contextual info.
Each error that happens signifies a possibility for fixing a bug or bettering the code. Don’t let this chance go by unnoticed.
Logging errors with log.Deadly()
In case your utility encounters an unrecoverable error, it’d really feel pure to log this error by calling log.Deadly(), which conveniently logs a message and exits the method instantly.
Nevertheless, there’s a catch. log.Deadly() calls os.Exit(). Not like a name to panic(), os.Exit() just isn’t recoverable and skips all deferred features.
A superb apply is to put in writing func essential() in order that it doesn’t defer any features and name log.Deadly() or os.Exit() completely in essential().
Not contemplating error restoration
“Crash early” is nice recommendation in lots of circumstances. Crashing an app permits it to restart from a clear state. Nevertheless, crashing just isn’t at all times the most suitable choice.
- If an error is simple to recuperate from, crashing the entire utility is an overreaction.
- If a course of ensures most uptime, it’s higher to do your greatest to recuperate from the error slightly than disrupting the system with a restart.
- If a course of spawns goroutines, it’s usually enough to exit a single goroutine that observes an error situation.
http.ListenAndServe()is an instance of this technique. All incoming requests are dealt with in separate goroutines, and if one goroutine panics,ListenAndServe()recovers from that panic so that every one different concurrent handlers can proceed unaffected.
Backside line: functions could profit from well-designed error restoration, particularly if crashing early entails a substantial value of respawning the app.
Conclusion
Error dealing with in Go has only a few transferring components and is due to this fact fast to be taught. The true artwork of error dealing with entails figuring out the way to optimally reply to particular error conditions and the way to handle errors on their means up the decision chain.
On this information, you discovered about helpful error dealing with methods, greatest practices, particular error sorts, and customary errors to keep away from. Your acquired information and abilities will assist you to write code that’s maintainable and straightforward to troubleshoot. However have you learnt the way to deal with errors in Go securely? Learn our subsequent error dealing with information to search out out!

