1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package status implements errors returned by gRPC. These errors are 20 // serialized and transmitted on the wire between server and client, and allow 21 // for additional data to be transmitted via the Details field in the status 22 // proto. gRPC service handlers should return an error created by this 23 // package, and gRPC clients should expect a corresponding error to be 24 // returned from the RPC call. 25 // 26 // This package upholds the invariants that a non-nil error may not 27 // contain an OK code, and an OK code must result in a nil error. 28 package status 29 30 import ( 31 "context" 32 "errors" 33 "fmt" 34 35 spb "google.golang.org/genproto/googleapis/rpc/status" 36 37 "google.golang.org/grpc/codes" 38 "google.golang.org/grpc/internal/status" 39 ) 40 41 // Status references google.golang.org/grpc/internal/status. It represents an 42 // RPC status code, message, and details. It is immutable and should be 43 // created with New, Newf, or FromProto. 44 // https://godoc.org/google.golang.org/grpc/internal/status 45 type Status = status.Status 46 47 // New returns a Status representing c and msg. 48 func New(c codes.Code, msg string) *Status { 49 return status.New(c, msg) 50 } 51 52 // Newf returns New(c, fmt.Sprintf(format, a...)). 53 func Newf(c codes.Code, format string, a ...any) *Status { 54 return New(c, fmt.Sprintf(format, a...)) 55 } 56 57 // Error returns an error representing c and msg. If c is OK, returns nil. 58 func Error(c codes.Code, msg string) error { 59 return New(c, msg).Err() 60 } 61 62 // Errorf returns Error(c, fmt.Sprintf(format, a...)). 63 func Errorf(c codes.Code, format string, a ...any) error { 64 return Error(c, fmt.Sprintf(format, a...)) 65 } 66 67 // ErrorProto returns an error representing s. If s.Code is OK, returns nil. 68 func ErrorProto(s *spb.Status) error { 69 return FromProto(s).Err() 70 } 71 72 // FromProto returns a Status representing s. 73 func FromProto(s *spb.Status) *Status { 74 return status.FromProto(s) 75 } 76 77 // FromError returns a Status representation of err. 78 // 79 // - If err was produced by this package or implements the method `GRPCStatus() 80 // *Status` and `GRPCStatus()` does not return nil, or if err wraps a type 81 // satisfying this, the Status from `GRPCStatus()` is returned. For wrapped 82 // errors, the message returned contains the entire err.Error() text and not 83 // just the wrapped status. In that case, ok is true. 84 // 85 // - If err is nil, a Status is returned with codes.OK and no message, and ok 86 // is true. 87 // 88 // - If err implements the method `GRPCStatus() *Status` and `GRPCStatus()` 89 // returns nil (which maps to Codes.OK), or if err wraps a type 90 // satisfying this, a Status is returned with codes.Unknown and err's 91 // Error() message, and ok is false. 92 // 93 // - Otherwise, err is an error not compatible with this package. In this 94 // case, a Status is returned with codes.Unknown and err's Error() message, 95 // and ok is false. 96 func FromError(err error) (s *Status, ok bool) { 97 if err == nil { 98 return nil, true 99 } 100 type grpcstatus interface{ GRPCStatus() *Status } 101 if gs, ok := err.(grpcstatus); ok { 102 grpcStatus := gs.GRPCStatus() 103 if grpcStatus == nil { 104 // Error has status nil, which maps to codes.OK. There 105 // is no sensible behavior for this, so we turn it into 106 // an error with codes.Unknown and discard the existing 107 // status. 108 return New(codes.Unknown, err.Error()), false 109 } 110 return grpcStatus, true 111 } 112 var gs grpcstatus 113 if errors.As(err, &gs) { 114 grpcStatus := gs.GRPCStatus() 115 if grpcStatus == nil { 116 // Error wraps an error that has status nil, which maps 117 // to codes.OK. There is no sensible behavior for this, 118 // so we turn it into an error with codes.Unknown and 119 // discard the existing status. 120 return New(codes.Unknown, err.Error()), false 121 } 122 p := grpcStatus.Proto() 123 p.Message = err.Error() 124 return status.FromProto(p), true 125 } 126 return New(codes.Unknown, err.Error()), false 127 } 128 129 // Convert is a convenience function which removes the need to handle the 130 // boolean return value from FromError. 131 func Convert(err error) *Status { 132 s, _ := FromError(err) 133 return s 134 } 135 136 // Code returns the Code of the error if it is a Status error or if it wraps a 137 // Status error. If that is not the case, it returns codes.OK if err is nil, or 138 // codes.Unknown otherwise. 139 func Code(err error) codes.Code { 140 // Don't use FromError to avoid allocation of OK status. 141 if err == nil { 142 return codes.OK 143 } 144 145 return Convert(err).Code() 146 } 147 148 // FromContextError converts a context error or wrapped context error into a 149 // Status. It returns a Status with codes.OK if err is nil, or a Status with 150 // codes.Unknown if err is non-nil and not a context error. 151 func FromContextError(err error) *Status { 152 if err == nil { 153 return nil 154 } 155 if errors.Is(err, context.DeadlineExceeded) { 156 return New(codes.DeadlineExceeded, err.Error()) 157 } 158 if errors.Is(err, context.Canceled) { 159 return New(codes.Canceled, err.Error()) 160 } 161 return New(codes.Unknown, err.Error()) 162 } 163