Checking for error message from os.Remove - go

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)

Related

Error handling: returning build-in error vs custom type

In Go's standard libraries, the built-in error is used for error handling. This post used some examples from the std libs to demonstrate how error handling works in Go.
package net
type Error interface {
error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
time.Sleep(1e9)
continue
}
if err != nil {
log.Fatal(err)
}
I am wondering what would be the benefits of sticking to this convention.
What if we return the custom type? e.g. net.Error type.
I assume that you are talking about the difference between DoRequest() error vs DoRequest() net.SomeSpecificError.
Following code
type CustomError struct {
}
func (CustomError) Error() string {
return "custom error"
}
func DoRequest() *CustomError {
return nil
}
func MarkTaskComplete() error {
return nil
}
func main() {
err := DoRequest()
if err != nil {
log.Fatal(err)
}
err = MarkTaskComplete()
if err != nil {
log.Fatal(err)
}
}
will fail to compile because error returned by MarkTaskComplete cannot be assigned to err variable which is of type *SomeError.
Second disadvantage is that you cannot return from DoRequest any other error than SomeError. There may be a few situations where you would benefit from such a restriction, but in general in most cases the approach with error is more flexible. Especially when your function calls another one.
It is also worth to read about the errors wrapping
The benefits of the customer type, ie net.Error is:
type Error interface {
error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}
The above has the advantage of being more descriptive. We get more information about the error like whether it is a Timeout error or a Temporary error.
In addition to that, we also get to know the primary data which is in the primary field "error", in the above example, which is a string.
type error interface {
Error() string
}

How to detect json.SyntaxError with errors.Is

I can't detect a json.SyntaxError while checking the error returned from a failed decoder.Decode operation!
Here you can see a working example in playground.
As you can see the debugger confirm to me it's a pointer to a json.SyntaxError, but errors.Is can't detect it.
I checked the errors.Is implementation:
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
for {
if isComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
// TODO: consider supporting target.Is(err). This would allow
// user-definable predicates, but also may allow for coping with sloppy
// APIs, thereby making it easier to get away with them.
if err = Unwrap(err); err == nil {
return false
}
}
}
And they are comparable (isComparable variable is true) but, when I would expect it to return true when it does if isComparable && err == target { it goes on...
What am I doing wrong?
Thanks in advance.
What is currently happening is that you compare the memory address of a new json.SyntaxError to the error returned from Decode. As you have noticed this will never be true.
What you want to do is a bit different: check if err is of type SyntaxError and then work with that object directly. This is possible using type assertions, which basically check if the underlying type of an interface (error in this case) is a more specific type.
This is what errors.As does. It populates a specific error type that you specify. Using this method, one lands at the following code:
if err != nil {
var serr *json.SyntaxError
if errors.As(err, &serr) {
fmt.Println("Syntax error:", serr)
fmt.Println("Offset:", serr.Offset)
} else {
fmt.Println("Other error:", err)
}
}

ERROR: need type assertion

I thought I have asserted (as far as I've learnt Go), but I keep getting this error
cannot use readBack["SomePIN"] (type interface {}) as type string in argument to c.String: need type assertion
Here is my code (this snippet is from a Request Handler function and I'm using Echo Web framework and Tiedot NoSQL database)
// To get query result document, simply
// read it [as stated in the Tiedot readme.md]
for id := range queryResult {
readBack, err := aCollection.Read(id)
if err != nil {
panic(err)
}
if readBack["OtherID"] == otherID {
if _, ok := readBack["SomePIN"].(string); ok {
return c.String(http.StatusOK, readBack["SomePIN"])
}
}
}
You are asserting readBack["SomePIN"] as a string - in the if statement. That doesn't make any change to readBack["SomePIN"], however - it's still an interface{}. In Go, nothing ever changes type. Here's what will work:
for id := range queryResult {
readBack, err := aCollection.Read(id)
if err != nil {
panic(err)
}
if readBack["OtherID"] == otherID {
if somePIN, ok := readBack["SomePIN"].(string); ok {
return c.String(http.StatusOK, somePIN)
}
}
}
You were tossing the string value from your type assertion, but you want it. So keep it, as somePIN, and then use it.
Final note - using the value, ok = interfaceVal.(type) syntax is a good practice. If interfaceVal turns out to be a non-string, you'll get value = "" and ok = false. If you eliminate the ok value from the type assertion and interfaceVal is a non-string, the program will panic.
It looks like your converting to a concrete type and throwing away the conversion, I think this should work:
if somePinString, ok := readBack["SomePIN"].(string); ok {
return c.String(http.StatusOK, somePinString)
}

Handling Aerospike library errors in Go Lang

I am quite new to Go Lang development. Recently I am using Aerospike Go client to getObject
err = aer.AeroDB.getObject(nil, key, Record)
if err != nil {
fmt.Println(err)
}
now the above error exposes only one method Error() which returns a string. I need to handle each type of errors differently. How do I do this, as there are no error codes returned. Do I do string matching to get the relevant type?
SOLUTION: Answer and comments below helped me to find an exact answer. I will share it here with the rest. Aerospike libraries export AerospikeError struct. Now, error could be nil or AerospikeError struct. Following code did the work.
import (
"errors"
"fmt"
aerospike "github.com/aerospike/aerospike-client-go"
"github.com/aerospike/aerospike-client-go/types"
)
type ArDB struct {
Host string
Port int
AeroDB *aerospike.Client
}
ArErr, failed := aer.AeroDB.GetObject(nil, key, Record).(types.AerospikeError)
if failed {
if ArErr.ResultCode() == types.KEY_NOT_FOUND_ERROR {
//Key is not present, create new data
Record = NewAudienceRecord()
} else {
//Handle other errors!
}
}
If the function returns errors which are actually of different type, then you should use type switch:
switch err.(type) {
case Error1:
fmt.Println("Error1", err)
case Error2:
fmt.Println("Error2", err)
default:
fmt.Println(err)
}
But if the errors are of same type then you have to see does the package export some "common errors" as variables, so that you can check against those:
if err == aer.Error1 {
// do something specific to Error1
}
Aerospike Go client dev here. In my experience, the concise conditional typecast shown below is more readable, albeit being the same as your own:
if ae, ok := err.(AerospikeError); ok {
println(ae.ResultCode())
}

Error concrete type

I have a method CreateProduct(&Product) error that returns a value implementing error interface. It can be a gorm database error or my own error type.
Having the returned value, how can I know which type is the error?
err = api.ProductManager.CreateProduct(product)
if err != nil {
// TODO: how to distinguish that it is a validation error?
response.WriteHeader(422)
response.WriteJson(err)
return
}
You can do a type assertion, and act if the error returned is from the expected type:
if nerr, ok := err.(yourError); ok {
// do something
}
You can also do a type switch for multiple test
switch t := err.(type) {
case yourError:
// t is a yourError
case otherError :
// err is an otherError
case nil:
// err was nil
default:
// t is some other type
}
Note: a type assertion is possible even on nil (when err == nil):
the result of the assertion is a pair of values with types (T, bool).
If the assertion holds, the expression returns the pair (x.(T), true);
otherwise, the expression returns (Z, false) where Z is the zero value for type T
Here, the "zero value" of "Error" would be nil.
You can use type assertions to do that:
if gormError, ok := err.(gorm.RecordNotFound); ok {
// handle RecordNotFound error
}
if myError, ok := err.(MyError); ok {
// handle MyError
}
When dealing with multiple error cases, it can be useful to use type switches for that:
switch actualError := err.(type) {
case gorm.RecordNotFound:
// handle RecordNotFound
case MyError:
// handle MyError
case nil:
// no error
}
The same way you would go about with any other interface. Use type assertion or a type switch:
switch err := err.(type) {
case MyValidationError:
fmt.Printf("validation error")
case MyOtherTypeOfError:
fmt.Printf("my other type of error")
default:
fmt.Printf("error of type %T", err)
}
Go Playground: http://play.golang.org/p/5-OQ3hxmZ5

Resources