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