Chris Hopkins
Chris Hopkins

Reputation: 1001

Interpreting Golang Error codes

So most examples of go error handling I see just pass any errors back up the stack. At some point these need interpreting and this is what I am trying to do. Here's a snippet of my attempt:

    resp, err := http.Get(string(url))
    defer out_count.Dec()

    if err != nil {

           switch err {
            case http.ErrBodyReadAfterClose:
                    fmt.Println("Read after close error")
            case http.ErrMissingFile:
                    fmt.Println("Missing File")
            {some more cases here}
            case io.EOF:
                    fmt.Println("EOF error found")
            default:
                    fmt.Printf("Error type is %T\n", err)
                    panic(err)
            }
            return

This isn't working though for my current case(edited to remove url}:

ERROR: Failed to crawl "http://{removed URL}"
Error type is *url.Error
panic: Get http://{removed url}: EOF

goroutine 658 [running]:
runtime.panic(0x201868, 0x106352c0)
        /usr/lib/go/src/pkg/runtime/panic.c:279 +0x1a0
github.com/cbehopkins/grab/grab.crawl(0x10606210, 0x27, 0x105184b0, 0x105184e0, 0x10500460)

I can't figure out a way to get the switch statement to catch this error since the text of the error changes every time and has no explicit value I can catch against. (as the URL changes all the time). Now maybe I could do some sort of regex match in the case statement or sub-slice the error string, but that feels like a very bad way to solve this problem.

Any suggestions? There must be an idiomatic way to catch errors such as this surely?

Upvotes: 2

Views: 16270

Answers (2)

Sridhar
Sridhar

Reputation: 2532

You can implement the error interface and create your own errors which you may find easier to deal with.

For runtime errors a.k.a panic. You can recover by including recover() in a function that you think may panic. It get's called before a panicing function returns.

defer func() {
    if r := recover(); r != nil {
        if _, ok := r.(runtime.Error); ok {
                err = r.(error) //panic(r)
            }
            err = r.(error)
        }
    }()
}

Upvotes: 1

abhink
abhink

Reputation: 9116

The simplest way would be to have package level error values in your code:

var URLFetchError = errors.New("Cannot fetch URL")

url := "http://www.google.com"
res, err := http.Get(url)
if err != nil {
    return URLFetchError
}

The switch then becomes:

switch err {
case http.ErrBodyReadAfterClose:
    fmt.Println("Read after close error")
case URLFetchError:
    fmt.Println("Error fetching URL")

If you want to pass more information with the error, you can create your own custom errors:

type MyError struct {
    URL string
}

func (e MyError) Error() string {
    return fmt.Sprintf("Error getting: %v", e.URL)
}

Then, you can create this error whenever required. For example:

url := "http://www.google.com"
res, err := http.Get(url)
if err != nil {
    return MyError{url}
}

Finally, in your error checking method, you can use type switches instead of simple switches to get the error:

switch err.(type) {
case MyError:
    fmt.Println("Error:", err)
default:
    fmt.Println("No Error")
}

In your case, since you have a mix of regular error, you can include this check in a nested switch:

switch err {
case http.ErrBodyReadAfterClose:
    fmt.Println("Read after close error")
case http.ErrMissingFile:
    fmt.Println("Missing File")
case io.EOF:
    fmt.Println("EOF error found")
default: // check for custom errors
    switch err.(type) {
    case MyError:
        fmt.Println("custom error:", err)
    default:
        panic(err)
    }
}

Upvotes: 8

Related Questions