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.
28package status
29
30import (
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
45type Status = status.Status
46
47// New returns a Status representing c and msg.
48func New(c codes.Code, msg string) *Status {
49 return status.New(c, msg)
50}
51
52// Newf returns New(c, fmt.Sprintf(format, a...)).
53func 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.
58func Error(c codes.Code, msg string) error {
59 return New(c, msg).Err()
60}
61
62// Errorf returns Error(c, fmt.Sprintf(format, a...)).
63func 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.
68func ErrorProto(s *spb.Status) error {
69 return FromProto(s).Err()
70}
71
72// FromProto returns a Status representing s.
73func 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.
96func 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.
131func 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.
139func 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.
151func 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}