For example, I am using one Go standard library function as:
func Dial(network, address string) (*Client, error)
This function may return errors, and I just care about errors which report "connection lost" or "connection refused", then do some code to fix these.
It seems like:
client, err := rpc.Dial("tcp", ":1234")
if err == KindOf(ConnectionRefused) {
// do something
}
What's more, how to get all the errors a specific standard library function may return?
There's no standard way to do this.
The most obvious way, which should only be used if no other method is available, is to compare the error string against what you expect:
if err.Error() == "connection lost" { ... }
Or perhaps more robust in some situations:
if strings.HasSuffix(err.Error(), ": connection lost") { ... }
But many libraries will return specific error types, which makes this much easier.
In your case, what's relevant are the various error types exported by the net package: AddrError, DNSConfigError, DNSError, Error, etc.
You probably care most about net.Error, which is used for network errors. So you could check thusly:
if _, ok := err.(net.Error); ok {
// You know it's a net.Error instance
if err.Error() == "connection lost" { ... }
}
What's more, how to get all the errors a specific standard library function may return?
The only fool-proof way to do this is to read the source for the library. Before going to that extreme, a first step is simply to read the godoc, as in the case of the net package, the errors are pretty well documented.
You can now use the errors.Is() function to compare some standard errors:
client, err := net.Dial("tcp", ":1234")
if errors.Is(err, net.ErrClosed) {
fmt.Println("connection has been closed.")
}
A common file opening scenario:
file, err := os.Open("non-existing");
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("file does not exist")
} else {
fmt.Println(err)
}
}
UPDATE
You can also use errors.As() if you'd like to check the error type:
client, err := net.Dial("tcp", ":1234")
var errC = net.ErrClosed
if errors.As(err, &errC) {
fmt.Printf("connection has been closed: %s", errC)
}
client.Close()
A more detailed explanation can be found in the Go Blog.
Related
Lets say I have the following function I'd like to write a test for:
func GetBootTime() (time.Time, error) {
currentTime := time.Now()
var info unix.Sysinfo_t
if err := unix.Sysinfo(&info); err != nil {
return time.Time{}, fmt.Errorf("error getting system uptime: %s", err)
}
return currentTime.Add(-time.Duration(info.Uptime) * time.Second).Truncate(time.Second), nil
}
How can I get unix.Sysinfo to return an error?
You cannot.
But you can "abstract it away" in one way or another.
For instance, you can have
var sysInfo := unix.SysInfo
func GetBootTime() (time.Time, error) {
currentTime := time.Now()
var info unix.Sysinfo_t
if err := sysInfo(&info); err != nil {
return time.Time{}, fmt.Errorf("error getting system uptime: %s", err)
}
return currentTime.Add(-time.Duration(info.Uptime) * time.Second).Truncate(time.Second), nil
}
…and then in your testing code have something like
sysInfo = func (_ *unix.Sysinfo_t) error {
return syscall.Errno(42)
}
before the actual test runs.
Note that this patching must be synchronized with other goroutines which may run testing code which ultimately calls into this function.
Note that there exist more hard-core appoaches which abstract away whole subsystems — such as github.com/spf13/afero which abstracts away all the filesystem operations available via the os and path/filepath packages and github.com/LopatkinEvgeniy/clock which abstracts away most functions from the time package.
With such an approach, you write all your code in a way so that it uses a single object implementing a particular interface, to carry out certain class of tasks, and at runtime, this object is either a "real" one or a "fake" one — when you do testing.
I'm trying to figure out a way to get the error cause while JSON decoding http.Response.Body
if err := json.NewDecoder(resp.Body).Decode(&lResp); err != nil {
// Get the cause of err
}
The type of err (and errors.Cause(err) using either github.com/pkg/errors or github.com/friendsofgo/errors) is *errors.errorString.
So what I'm able to do right now is the exact opposite of checking for the error type, namely:
if strings.HasSuffix(cause.Error(), "(Client.Timeout exceeded while reading body)") {
//...
}
I can try to use ioutil.ReadAll() and then I'll get *http.httpError as an error when the timeout occurs.
The primary reason is that I don't want to get the partially read JSON structure in the error - only the cause of the error and with current way, it's being done (the error returned) I get:
main.ListingResponse.DataSources: struct CustomType{ /* partially read JSON struct ... */ }.net/http: request canceled (Client.Timeout exceeded while reading body)
Ok, so I ended up reading the response body into into a []byte and then unmarshalling it with json.Unmarshal()
bb, err := ioutil.ReadAll(resp.Body)
if err != nil {
var netError net.Error
if errors.As(err, &netError) {
log.Printf("netError %v", netError)
// handle net.Error...
return nil, netError
}
// handle general errors...
return nil, netError
}
var lResp LResponse
if err := json.Unmarshal(bb, &lResp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal LResponse")
}
I'm still looking for a solution to use json.NewDecoder(resp.Body).Decode(&str) to avoid copying whole body into memory.
If anyone knows the way to do it, please add your answer.
What's the most idiomatic way to check error messages? My use case is that in err := os.Remove(path), I consider a success either:
A) if err == nil
or
B) if err != nil but the error is thrown because the file was not found.
any other error should cause the removal to retry. Currently I've wrapped this in a for { ... } loop and am checking:
if err == nil || strings.Contains(err.Error(), "no such file") {
// Success
} else {
// Fail
}
Since the docs say:
If there is an error, it will be of type *PathError.
I don't think there's a way to check by type assertion. Am I missing something fundamental? My error handling in Go has always felt somewhat slapdash.
I just dealt with this the other day. An error from os.Remove() will be syscall.ENOENT if the file did not exist.
So you can use logic like this:
if err != nil {
e, ok := err.(*os.PathError)
if ok && e.Err == syscall.ENOENT {
// The file didn't exist
w.WriteHeader(http.StatusNotFound)
return
} else {
// Error other than syscall.ENOENT
}
}
Of course, as shown in another answer, os.IsNotExist() is simple and idiomatic. Wish I'd seen that before today.
The "type" error is an interface. Interfaces don't have an concrete type. To get the type of the value you could use a type assertion or a type switch:
// Type assertion
_, ok := err.(*PathError)
// Type switch
switch err.(type) {
case *PathError:
// You know the type now
}
This is an idiomatic way to find out of which type an error is. As in the comments specified there is already a function inside the os package which does this for you (https://golang.org/pkg/os/#IsNotExist)
I have some trouble with go routines and channels regarding error handling.
Firstly I have a function that listen for messages (in a infinite for loop):
func main() {
messageChannel := make(chan messageHandler.MessageInfo)
for {
if token := client.Subscribe("#", 0, func(client MQTT.Client, msg MQTT.Message) {
go messageHandler.DecodeMessage(msg, messageChannel)
select {
case messageInfo := <-messageChannel:
//Handle
}
}); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
}
}
}
But in the DecodeMessage function, there could arise multiple errors.
func DecodeMessage(msg mqtt.Message, c1 chan MessageInfo) {
//do something, might result in error
//do another thing, might result in error
c1 <- MessageInfo{...}
}
Normally I would just return from the function. But seems a bit trickier with routines. I've looked at this post, but if both errors would occur, I would only see the last error message.
Example:
func DecodeMessage(msg mqtt.Message, c1 chan MessageInfo) {
var returnError error
if err != nil {
returnError = err
}
if err != nil {
returnError = err
}
c1 <- MessageInfo{
Error: returnError,
...
}
}
Should I have an array of some sort and append all errors? Is it bad practice to have multiple errors in one routine?
The best thing, for me, is that the routine would exit on an error and return that error like it would do "normally". Is that possible?
I'll start by saying that having to go through all the error checks for a function before returning even if they fail is a bit of a code smell. It might mean that you have something weird going on and there may be a better way to accomplish what you're trying to do.
However, assuming that you've boiled down your problem to this, then I see two options, depending on how the errors have to be handled.
If the errors can be handled one-by-one and don't really rely on each other, then you can just create an error channel and send them back one by one as you encounter the errors. See the following working example:
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
errCh := make(chan error)
go HandleErrorsSerially("bad error", errCh)
for err := range errCh {
fmt.Printf("Found error serially: %v\n", err)
}
}
func HandleErrorsSerially(msg string, errCh chan<- error) {
if strings.Contains(msg, "error") {
errCh <- errors.New("message contained string 'error'")
}
if strings.Contains(msg, "bad") {
errCh <- errors.New("message contained string 'bad'")
}
close(errCh)
}
Alternatively, if you need to have a view of all the errors that occurred all at once (because two errors happening simultaneously may indicate some special circumstances) then you'd have to append them all to an array and then pass them through a channel. See the following working example:
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
errArrCh := make(chan []error)
go HandleErrorsTogether("bad error", errArrCh)
errArr := <-errArrCh
fmt.Printf("Found the following errors together: %v\n", errArr)
}
func HandleErrorsTogether(msg string, errArrCh chan<- []error) {
errArr := make([]error, 0)
if strings.Contains(msg, "error") {
errArr = append(errArr, errors.New("message contained string 'error'"))
}
if strings.Contains(msg, "bad") {
errArr = append(errArr, errors.New("message contained string 'bad'"))
}
errArrCh <- errArr
close(errArrCh)
}
I can see cases where it's useful to return multiple errors, such as when you are parsing messages and there are multiple bad fields and you need to summarise these back to a client.
I think the best approach is to use a package like hashicorp's multierror which allows multiple errors to be collected with formatting in a structure type that implements error interface and so can still be sent on a chan error. The receive side can then either process as just a standard error or extract the information on each individual error.
The multierror documentation is pretty good, just read through the examples on the github page.
The best thing, for me, is that the routine would exit on an error and
return that error like it would do "normally".
Of course, you can, and get last error or all error are both pretty strange.
Get last error is unhelpful for debugging, compare with getting first error.
Get all error is same if the first error will cause the following failure, the following error message is unhelpful; Another situation is these errors do not have the association, I think this means you have to sperate they into different concurrency part for better controlling.
Ok, now back to original problem. Consider the code:
func foo(errTo chan error) {
defer close(errTo)
v, err := CouldFailOne()
if err != nil {
errTo <- err
return // Yp, just stop this routine, let it join back to Invoker
}
v2, err := CloudFailTwo()
if err != nil {
errTo <- err
return
}
// As the previous error handle until end of the function
}
If you want to return value from this kind of function. Just use a channel, and send the value into it only no error raise. I think this style will be more clear and like return style, just became using a channel to return the error.
I want to check the error type from a function I call to see if it was caused by a deadlineExceededError but I don't see a way to reference it. I suppose I can always check against the .Error() string but I've been told that's frowned upon.
Also it's set to 2 microseconds for debugging purposes, I realize it should be changed to time.Minute
Godoc for the function in question: https://godoc.org/github.com/moby/moby/client#Client.ContainerStart
//if the container fails to start after 2 minutes then we should timeout
ctx, cancel := context.WithTimeout(ctx, 2*time.Microsecond)
defer cancel()
// Do the actual start
if err := myClient.ContainerStart(ctx, containerName, types.ContainerStartOptions{}); err != nil {
fmt.Printf("%v\n", err) //prints: 'context deadline exceeded'
fmt.Printf("%T\n", err) //prints: 'context.deadlineExceededError'
switch e := err.(type) {
case //how do I check for deadlineExceededError:
//print that it timed out here
}
return err
}
The context package exposes this value as a variable.
You can compare err == context.DeadlineExceeded.
However, as argued by Dave Cheney, you should probably use an interface instead.
Specifically net.Error or interface { Timeout() bool } will work as a type.