I was trying to figure out if an error was a wrapped grpc error with go 1.13 style errors (currently using the golang.org/x/xerrors package to fill that functionality).

Finding the underlying error will allow me to report it or not depending on the status. A NotFound error doesn't need to appear in our error reporting system.

However you can't access the actual error type for grpc errors since it is not a type the grpc/status package exports.

Basically I want to do:

```go if grpcCauseError := new(status.statusError); xerrors.As(err, &grpcCauseError) { errorCode := status.Code(grpcCauseError) switch errorCode { case codes.InvalidArgument, codes.NotFound, codes.AlreadyExists, codes.PermissionDenied, codes.Unimplemented, codes.Unauthenticated: // Don't report it default: // Report it } }

```

This won't work since I cannot access the status.statusError type. I tried making a new grpc error to match against:

go if grpcCauseError := grpc.Errorf(codes.Unknown, "error"); xerrors.As(err, &grpcCauseError) { errorCode := status.Code(grpcCauseError) switch errorCode { case codes.InvalidArgument, codes.NotFound, codes.AlreadyExists, codes.PermissionDenied, codes.Unimplemented, codes.Unauthenticated: // Don't report it default: // Report it } } But that also doesn't work since the grpcCauseError only ends up having the type error and all errors will match that interface so that if statement will always be successful.

The status.statusError type exports 2 methods though: Error() and GRPCStatus(). So we can make an interface that fits this:

go type grpcError interface { Error() string GRPCStatus() *status.Status } To be able to use it with xerrors.As() though, we'll need more than a pointer to an interface, we'll need an actual instance of that interface. This can be achieved by either converting an existing grpc error or creating a new type that matches that interface:

```go baseGrpcError := grpc.Errorf(codes.Unknown, "oops") grpcCauseError := e.(grpcError) if xerrors.As(err, &grpcCauseError) { errorCode := status.Code(grpcCauseError) switch errorCode { case codes.InvalidArgument, codes.NotFound, codes.AlreadyExists, codes.PermissionDenied, codes.Unimplemented, codes.Unauthenticated: // Don't report it default: // Report it } } // OR type mockGRPCError struct{}

func (mockGRPCError) Error() string { return "" } func (mockGRPCError) GRPCStatus() *status.Status { return &status.Status{} }

var grpcCauseError grpcError grpcCauseError = mockGRPCError{} if xerrors.As(err, &grpcCauseError) { errorCode := status.Code(grpcCauseError) switch errorCode { case codes.InvalidArgument, codes.NotFound, codes.AlreadyExists, codes.PermissionDenied, codes.Unimplemented, codes.Unauthenticated: // Don't report it default: // Report it } } In both cases, getting the base error can be abstracted in a function so that we get a bit cleaner code:go if grpcCauseError := getGrpcError(); xerrors.As(err, &grpcCauseError) { errorCode := status.Code(grpcCauseError) switch errorCode { case codes.InvalidArgument, codes.NotFound, codes.AlreadyExists, codes.PermissionDenied, codes.Unimplemented, codes.Unauthenticated: // Don't report it default: // Report it } }

func getGrpcError() *grpcError { baseGrpcError := grpc.Errorf(codes.Unknown, "oops") grpcCauseError := e.(grpcError) return grpcCauseError } ```