Interpreting Golang Error codes - go

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?

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

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

Related

How to make this InTx func (for SQL transactions) "safe" if there is a panic during callback?

Playground link: https://go.dev/play/p/laQo-BfF7sK
It's subtle, but this InTx "context manager" (in transaction) has at least one bug. If there is a panic during the "Fun" call:
type Fun func(context.Context, *sql.Tx) error
func InTx(db *sql.DB, fn Fun) error {
ctx := context.Background()
t, err := db.BeginTx(ctx, nil)
if err != nil {
log.Panicln(err)
return err
}
return safe(ctx, t, fn)
}
// safe should run the provided function in the context of a SQL transaction
// expect a nil error if (and only if) everything worked w/o incident
func safe(ctx context.Context, t *sql.Tx, fn Fun) (err error) {
defer func() {
if err == nil {
err = t.Commit()
return
}
if bad := t.Rollback(); bad != nil && bad != sql.ErrTxDone {
err = fmt.Errorf("during rollback, panic(%v); err=%w", bad, err)
// log error
return
}
}()
err = fn(ctx, t)
return
}
Here is an example to demonstrate:
func main() {
var db *sql.DB;
// ...
_ = InTx(db, func(ctx context.Context, t *sql.Tx) error {
// ... lots more SQL executed here ...
if _, err := t.Exec("DELETE FROM products"); err != nil {
return err
}
// ...
panic("will cause Commit")
// should expect Rollback() instead, as if we:
//return nil
})
}
Related: Would it be inappropriate to panic during another panic, e.g. if Rollback fails? If so, why? (or when not)
Adding recover in another defer (after the first one in the safe function, since they unwind in stack order) would guard against an "inner" panic from the callback, but that may be sub-optimal or less idiomatic that other approaches.
defer func() {
if veryBad := recover(); veryBad != nil {
bad := t.Rollback()
err = fmt.Errorf("aborted SQL due to panic: %v; err=%w", veryBad, bad)
// log error, should re-panic here?
return
}
}()
I'd be very happy to accept someone else's Go wisdom in lieu of my potentially-flawed approach.

Error Handling within a for loop in Go results probably in a next iteration

I struggle with a specific Go implementation for sending log files to different locations:
package main
func isDestinationSIEM(json_msg string, json_obj *jason.Object, siem_keys []string) (bool) {
if json_obj != nil {
dest, err := json_obj.GetString("destination")
if err == nil {
if strings.Contains(dest,"SIEM") {
return true
}
}
for _, key := range siem_keys {
if strings.Contains(json_msg, key) {
return true
}
}
}
return false
}
func sendToSIEM(siem_dst string, json_msg string) (error) {
// Create connection to syslog server
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
fmt.Println("failed to parse root certificate")
}
config := &tls.Config{RootCAs: roots, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", siem_dst, config)
if err != nil {
fmt.Println("Error connecting SIEM")
fmt.Println(err.Error())
} else {
// Send log message
_, err = fmt.Fprintf(conn, json_msg)
if err != nil {
fmt.Println("Error sending SIEM message: ", json_msg)
fmt.Println(err.Error())
}
}
defer conn.Close()
return err
}
func main() {
// simplified code otherwise there would have been too much
// but the 'devil' is this for loop
for _, obj := range objects {
// first check
isSIEM := isDestinationSIEM(obj, siem_keys)
if isSIEM {
err := sendToSIEM(obj)
if err != nil {
// print error
}
isAUDIT:= isDestinationSIEM(obj)
if isAUDIT {
err := sendToAUDIT(obj)
if err != nil {
// print error
}
} // end of for
}
When the 'if isSIEM' returns an error, the second check 'if isAUDIT' is not conducted.
Why is this? If an error is returned, does the loop start with the next iteration?
The error looks like this:
runtime error: invalid memory address or nil pointer dereference: errorString (which lists a couple of go packages)
The error looks like this: runtime error: invalid memory address or nil pointer dereference: errorString (which lists a couple of go packages)
It means you catch the panic() and your program has been stopped that means your circle for is stopped too.
Here details how works with panic https://blog.golang.org/defer-panic-and-recover

Golang panic: runtime error: invalid memory address or nil pointer dereference

i'm new to golang and it may be a very basic thing but i can't seems to find the solution.
the request return json which is like this.
{"uuid":"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22","name":"core1","owner":"systems","description":"new","creation_date":"2017-06-10T14:20:00Z"}
This is the gocode.
package main
import (
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type Project struct {
Uuid string `json:"uuid"`
Name string `json:"name"`
Owner string `json:"owner"`
Description string `json:"description"`
Creation_date string `json:"creation_date"`
}
func main() {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
req, err := http.NewRequest("GET", "https://localhost:4443/project/core1", nil)
req.SetBasicAuth("rohit", "rohit")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("server not responding %s", err.Error())
}
var p Project
b, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
err = json.Unmarshal(b, &p)
if err != nil {
fmt.Printf("Test case failed with error %s", err.Error())
}
if resp.StatusCode != 403 {
fmt.Printf("failed %s", err.Error())
}
}
after running i'm getting this error
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x40142f]
goroutine 1 [running]:
panic(0x60c860, 0xc42000c130)
/usr/local/go/src/runtime/panic.go:500 +0x1a1
main.main()
/home/rohitk/Go_projects/src/first_program/test/main.go:41 +0x42f
exit status 2
i checked and response body has right data.
can someone please suggest what's happening here.Thanks!
As mentioned by commenters, your code is only printing errors, not handling them by altering the behavior of the program.
resp, err := client.Do(req)
if err != nil {
fmt.Printf("server not responding %s", err.Error())
}
// ...
b, err := ioutil.ReadAll(resp.Body)
In the snippet above, if there was an error then it gets printed; however, flow control proceeds as usual even though the "resp" object is probably not valid (e.g. nil).
When a library program encounters an error you should usually return it immediately without any further action. For end-user applications, you should usually display the error (typically on the stderr stream) and exit the program (typically with a nonzero exit code). For example:
resp, err := client.Do(req)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
os.Exit(1) // Exit the program if we didn't get a response.
}
// ...
b, err := ioutil.ReadAll(resp.Body)
I am just seeing this question and I just wanted to contribute.
As mentioned by #maerics.As mentioned by commenters, your code is only printing errors, not handling them by altering the behavior of the program. My observation is also that there are two places that you are printing out errors and not handling them.
if err != nil {
fmt.Printf("server not responding %s", err.Error())
}
It should be:
if err != nil {
fmt.Printf("server not responding %s", err.Error())
return // the return statement here helps to handle the error
}
Also the second one which is :
if err != nil {
fmt.Printf("Test case failed with error %s", err.Error())
}
It should rather be :
if err != nil {
fmt.Printf("Test case failed with error %s", err.Error())
return // the return statement here helps to handle the error
}

Should I error-check Close() on a response body?

The docs for net/http have the following example:
resp, err := http.Get("http://example.com/")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("%s", body)
Close returns an error, but it is not checked. Is there something I'm missing here? The importance of checking every error is frequently emphasized in go, but I see this defer resp.Body.Close() pattern a lot with no error checks.
There are two things to consider: What would you do with it if you checked it and there was an error? And, what would the side-effects be if there was an error?
In most cases, for closing a response body, the answer to both questions is... absolutely nothing. If there's nothing you'd do if there was an error and the error has no appreciable impact, there's no reason to check it.
Also note that Close() returns an error in order to fulfill the io.Closer interface; the function doesn't necessarily return an error. You'd need to check the source to know for sure if it has an error case.
This is a downside of using defer
It would be advised, as a responsible developer, that you check for all possible error prone points, and handle them as gracefully as you can.
Here are some of the options you can choose in handling this situation:
Option #1
Do not use defer, instead manually call close once you're done with the response's body and simply check for errors then.
Option #2
Create an anonymous function that wraps the closing and error checking code. Something like this:
defer func() {
err := resp.Body.Close()
if err != nil {
log.Fatal(err)
}
}()
Avoid using panics in your programs. Try to handle the errors gracefully by doing something or at least logging the error.
Additional information can be found here.
To add to #Mihailo option #2, call it option #2.1
Define function dclose() like so:
func dclose(c io.Closer) {
if err := c.Close(); err != nil {
log.Fatal(err)
}
}
use like so:
defer dclose(resp.Body)
Also in your code the check for err!=nil can declare:
func errcheck(err error) {
if err != nil {
log.Fatal(err)
}
}
then use:
errcheck(err)
then your code becomes:
resp, err := http.Get("http://example.com/")
errcheck(err)
defer dclose(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
errcheck(err)
fmt.Printf("%s", string(body))
IMHO a little cleaner perhaps? But I'll wait for the go aficionado's to correct me and highlight drawbacks.
EDIT
Thanks! #RayfenWindspear
Do replace log.Fatal(err) with log.Println(err) to avoid unnecessary panic.
EDIT2
dclose() to avoid confusion with go close()
Have fun!
From what I could gather, net/http can't err. However, I would rather assume that .Close() can err for any implementation of io.Closer than to study the internals.
Below is an example using named returns where defer only sets the returned error if the error would otherwise be nil:
func printResponse(url string) (retErr error) {
resp, err := http.Get(url)
if err != nil {
return err
}
defer func() {
err := resp.Body.Close()
if err != nil && retErr == nil {
retErr = err
}
}()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Printf("%s", body)
return nil
}

Whats the correct Go way to do handle errors

This seems a bit stupid, surely theres a better way?
err = SendMessageAndWait(db, "this is a test")
if err != nil {
fmt.Println("Error sending message", err)
return
}
err = DoSomething(db, "this is a test")
if err != nil {
fmt.Println("Error sending message", err)
return
}
err = CheckSomething(db, "this is another test")
if err != nil {
fmt.Println("Error sending message", err)
return
}
err = SendMessageAndWait(db, "this is a third test")
if err != nil {
fmt.Println("Error sending message", err)
return
}
... x10 ...
Update: For the record, 5 years on from when I wrote this, I am now persuaded that this is a completely sufficient, and perhaps even better, way to handle errors clearly. Not saying its pretty though.
Sadly that's the way it is in Go, however in a way you can make it cleaner:
func isError(err error, pre string) error {
if err != nil {
log.Printf("%v: %v", pre, err)
}
return err
}
func isErrorBool(err error, pre string) (b bool) {
if err != nil {
log.Printf("%v: %v", pre, err)
b = true
}
return
}
func checkSomething() error {
return nil
}
func main() {
if err := isError(checkSomething(), "something failed"); err != nil {
return /* err */
}
//if you don't want to return the error, just check it and die.
if isErrorBool(checkSomething(), "something else failed") {
return
}
}
I would not just print an error and return nothing: the idea is to act on the error and return it (if no decisive action was taken, like a simple log).
Simply calling return is like ignoring the error completely as far as the rest of the application is concerned.
See "Best Practices for Errors in Go", which includes advices as:
Predefine errors
Given a small set of errors, the best way to handle this is to predefine each error publicly at the package level.
Provide information
custom error type is the best solution to this problem. Go's implicit interfaces make creating one easy
Provide stack traces
package errgo provides the functionality of wrapping an error into another one that records where the error happened.
(You have the same features in dropbox/godropbox/errors/errors.go)
In Go, always check for errors. For example,
package main
import "fmt"
func doStuff() error {
err := SendMessageAndWait(db, "this is a test")
if err != nil {
return err
}
err = DoSomething(db, "this is a test")
if err != nil {
return err
}
err = CheckSomething(db, "this is another test")
if err != nil {
return err
}
err = SendMessageAndWait(db, "this is a third test")
if err != nil {
return err
}
return nil
}
func main() {
err := doStuff()
if err != nil {
fmt.Println("Error sending message", err)
}
}
Given your lack of context, I can only assume you're returning from func main().
http://play.golang.org/p/pgcwMb647A
package main
import (
"fmt"
"log"
"errors"
)
func foo(x int) error {
if x == 3 {
return errors.New("x == 3")
}
fmt.Println(x)
return nil
}
func main() {
check := func(err error) {
if err != nil {
log.Fatal(err)
}
}
check(foo(1))
check(foo(2))
check(foo(3))
check(foo(4))
}
Generally, explicit handling is the way to go, but there's a variety of things you can do depending on the context.
At the risk of turning this into code golf, Go supports single line if statements with assignments in them:
if err := SendMessageAndWait(db, "this is a test"); err != nil {
return err
}
The downside is that all return values assigned are scoped to the corresponding if/else if/else block, so if you actually need a different returned value outside that block, you have to go with something closer to PeterSO's answer.

Resources