github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/common/rpc/errors.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/hashicorp/go-multierror" 8 "google.golang.org/grpc/codes" 9 "google.golang.org/grpc/status" 10 11 "github.com/onflow/flow-go/module/state_synchronization/indexer" 12 "github.com/onflow/flow-go/storage" 13 ) 14 15 // ConvertError converts a generic error into a grpc status error. The input may either 16 // be a status.Error already, or standard error type. Any error that matches on of the 17 // common status code mappings will be converted, all unmatched errors will be converted 18 // to the provided defaultCode. 19 func ConvertError(err error, msg string, defaultCode codes.Code) error { 20 if err == nil { 21 return nil 22 } 23 24 // Handle multierrors separately 25 if multiErr, ok := err.(*multierror.Error); ok { 26 return ConvertMultiError(multiErr, msg, defaultCode) 27 } 28 29 // Already converted 30 if status.Code(err) != codes.Unknown { 31 return err 32 } 33 34 if msg != "" { 35 msg += ": " 36 } 37 38 var returnCode codes.Code 39 switch { 40 case errors.Is(err, context.Canceled): 41 returnCode = codes.Canceled 42 case errors.Is(err, context.DeadlineExceeded): 43 returnCode = codes.DeadlineExceeded 44 default: 45 returnCode = defaultCode 46 } 47 48 return status.Errorf(returnCode, "%s%v", msg, err) 49 } 50 51 // ConvertStorageError converts a generic error into a grpc status error, converting storage errors 52 // into codes.NotFound 53 func ConvertStorageError(err error) error { 54 if err == nil { 55 return nil 56 } 57 58 // Already converted 59 if status.Code(err) == codes.NotFound { 60 return err 61 } 62 63 if errors.Is(err, storage.ErrNotFound) { 64 return status.Errorf(codes.NotFound, "not found: %v", err) 65 } 66 67 return status.Errorf(codes.Internal, "failed to find: %v", err) 68 } 69 70 // ConvertIndexError converts errors related to index and storage to appropriate gRPC status errors. 71 // If the error is nil, it returns nil. If the error is not recognized, it falls back to ConvertError 72 // with the provided default message and Internal gRPC code. 73 func ConvertIndexError(err error, height uint64, defaultMsg string) error { 74 if err == nil { 75 return nil 76 } 77 78 if errors.Is(err, indexer.ErrIndexNotInitialized) { 79 return status.Errorf(codes.FailedPrecondition, "data for block is not available: %v", err) 80 } 81 82 if errors.Is(err, storage.ErrHeightNotIndexed) { 83 return status.Errorf(codes.OutOfRange, "data for block height %d is not available", height) 84 } 85 86 if errors.Is(err, storage.ErrNotFound) { 87 return status.Errorf(codes.NotFound, "data not found: %v", err) 88 } 89 90 return ConvertError(err, defaultMsg, codes.Internal) 91 } 92 93 // ConvertMultiError converts a multierror to a grpc status error. 94 // If the errors have related status codes, the common code is returned, otherwise defaultCode is used. 95 func ConvertMultiError(err *multierror.Error, msg string, defaultCode codes.Code) error { 96 allErrors := err.WrappedErrors() 97 if len(allErrors) == 0 { 98 return nil 99 } 100 101 if msg != "" { 102 msg += ": " 103 } 104 105 // get a list of all of status codes 106 allCodes := make(map[codes.Code]struct{}) 107 for _, err := range allErrors { 108 allCodes[status.Code(err)] = struct{}{} 109 } 110 111 // if they all match, return that 112 if len(allCodes) == 1 { 113 code := status.Code(allErrors[0]) 114 return status.Errorf(code, "%s%v", msg, err) 115 } 116 117 // if they mostly match, ignore Unavailable and DeadlineExceeded since any other code is 118 // more descriptive 119 if len(allCodes) == 2 { 120 if _, ok := allCodes[codes.Unavailable]; ok { 121 delete(allCodes, codes.Unavailable) 122 for code := range allCodes { 123 return status.Errorf(code, "%s%v", msg, err) 124 } 125 } 126 if _, ok := allCodes[codes.DeadlineExceeded]; ok { 127 delete(allCodes, codes.DeadlineExceeded) 128 for code := range allCodes { 129 return status.Errorf(code, "%s%v", msg, err) 130 } 131 } 132 } 133 134 // otherwise, return the default code 135 return status.Errorf(defaultCode, "%s%v", msg, err) 136 }