2

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?

2 Answers 2

8

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)
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the exhaustive answer! I think the problem is that the error I am getting frm the standard html package does not provide these error defines I can match against. But you are correct that I could switch against the error type first and then on error detail later. For this application that will be enough, I now just need to figure out how to feed this request back into the standard html package. Thanks
1

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)
        }
    }()
}

2 Comments

Ah, I think you have misunderstood, the panic is there for me to stop the code when there is an error my code can't handle. It provides me wit the debug to say I'm not catching all the problems I should. Thanks though
Perhaps you didn't understand what "Implement the error interface" means because that's what the answer you chose does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.