blob: bcf2e4d81beb31be044a63bb64e511bc9684712f [file] [log] [blame]
khenaidoo5fc5cea2021-08-11 17:39:16 -04001/*
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"
khenaidoo257f3192021-12-15 16:46:37 -050032 "errors"
khenaidoo5fc5cea2021-08-11 17:39:16 -040033 "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 ...interface{}) *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 ...interface{}) 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
khenaidoo5cb0d402021-12-08 14:09:16 -050077// FromError returns a Status representation of err.
78//
Joey Armstrongba3d9d12024-01-15 14:22:11 -050079// - If err was produced by this package or implements the method `GRPCStatus()
Akash Kankanala761955c2024-02-21 19:32:20 +053080// *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.
khenaidoo5cb0d402021-12-08 14:09:16 -050084//
Akash Kankanala761955c2024-02-21 19:32:20 +053085// - 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.
khenaidoo5cb0d402021-12-08 14:09:16 -050092//
Joey Armstrongba3d9d12024-01-15 14:22:11 -050093// - 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.
khenaidoo5fc5cea2021-08-11 17:39:16 -040096func FromError(err error) (s *Status, ok bool) {
97 if err == nil {
98 return nil, true
99 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530100 type grpcstatus interface{ GRPCStatus() *Status }
101 if gs, ok := err.(grpcstatus); ok {
102 if gs.GRPCStatus() == nil {
103 // Error has status nil, which maps to codes.OK. There
104 // is no sensible behavior for this, so we turn it into
105 // an error with codes.Unknown and discard the existing
106 // status.
107 return New(codes.Unknown, err.Error()), false
108 }
109 return gs.GRPCStatus(), true
110 }
111 var gs grpcstatus
112 if errors.As(err, &gs) {
113 if gs.GRPCStatus() == nil {
114 // Error wraps an error that has status nil, which maps
115 // to codes.OK. There is no sensible behavior for this,
116 // so we turn it into an error with codes.Unknown and
117 // discard the existing status.
118 return New(codes.Unknown, err.Error()), false
119 }
120 p := gs.GRPCStatus().Proto()
121 p.Message = err.Error()
122 return status.FromProto(p), true
khenaidoo5fc5cea2021-08-11 17:39:16 -0400123 }
124 return New(codes.Unknown, err.Error()), false
125}
126
127// Convert is a convenience function which removes the need to handle the
128// boolean return value from FromError.
129func Convert(err error) *Status {
130 s, _ := FromError(err)
131 return s
132}
133
Akash Kankanala761955c2024-02-21 19:32:20 +0530134// Code returns the Code of the error if it is a Status error or if it wraps a
135// Status error. If that is not the case, it returns codes.OK if err is nil, or
136// codes.Unknown otherwise.
khenaidoo5fc5cea2021-08-11 17:39:16 -0400137func Code(err error) codes.Code {
138 // Don't use FromError to avoid allocation of OK status.
139 if err == nil {
140 return codes.OK
141 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530142
143 return Convert(err).Code()
khenaidoo5fc5cea2021-08-11 17:39:16 -0400144}
145
khenaidoo257f3192021-12-15 16:46:37 -0500146// FromContextError converts a context error or wrapped context error into a
147// Status. It returns a Status with codes.OK if err is nil, or a Status with
148// codes.Unknown if err is non-nil and not a context error.
khenaidoo5fc5cea2021-08-11 17:39:16 -0400149func FromContextError(err error) *Status {
khenaidoo257f3192021-12-15 16:46:37 -0500150 if err == nil {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400151 return nil
khenaidoo5fc5cea2021-08-11 17:39:16 -0400152 }
khenaidoo257f3192021-12-15 16:46:37 -0500153 if errors.Is(err, context.DeadlineExceeded) {
154 return New(codes.DeadlineExceeded, err.Error())
155 }
156 if errors.Is(err, context.Canceled) {
157 return New(codes.Canceled, err.Error())
158 }
159 return New(codes.Unknown, err.Error())
khenaidoo5fc5cea2021-08-11 17:39:16 -0400160}