// Copyright 2021-2025 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package connect import ( "fmt" "strconv" "strings" ) // A Code is one of the Connect protocol's error codes. There are no user-defined // codes, so only the codes enumerated below are valid. In both name and // semantics, these codes match the gRPC status codes. // // The descriptions below are optimized for brevity rather than completeness. // See the [Connect protocol specification] for detailed descriptions of each // code and example usage. // // [Connect protocol specification]: https://connectrpc.com/docs/protocol type Code uint32 const ( // The zero code in gRPC is OK, which indicates that the operation was a // success. We don't define a constant for it because it overlaps awkwardly // with Go's error semantics: what does it mean to have a non-nil error with // an OK status? (Also, the Connect protocol doesn't use a code for // successes.) // CodeCanceled indicates that the operation was canceled, typically by the // caller. CodeCanceled Code = 1 // CodeUnknown indicates that the operation failed for an unknown reason. CodeUnknown Code = 2 // CodeInvalidArgument indicates that client supplied an invalid argument. CodeInvalidArgument Code = 3 // CodeDeadlineExceeded indicates that deadline expired before the operation // could complete. CodeDeadlineExceeded Code = 4 // CodeNotFound indicates that some requested entity (for example, a file or // directory) was not found. CodeNotFound Code = 5 // CodeAlreadyExists indicates that client attempted to create an entity (for // example, a file or directory) that already exists. CodeAlreadyExists Code = 6 // CodePermissionDenied indicates that the caller doesn't have permission to // execute the specified operation. CodePermissionDenied Code = 7 // CodeResourceExhausted indicates that some resource has been exhausted. For // example, a per-user quota may be exhausted or the entire file system may // be full. CodeResourceExhausted Code = 8 // CodeFailedPrecondition indicates that the system is not in a state // required for the operation's execution. CodeFailedPrecondition Code = 9 // CodeAborted indicates that operation was aborted by the system, usually // because of a concurrency issue such as a sequencer check failure or // transaction abort. CodeAborted Code = 10 // CodeOutOfRange indicates that the operation was attempted past the valid // range (for example, seeking past end-of-file). CodeOutOfRange Code = 11 // CodeUnimplemented indicates that the operation isn't implemented, // supported, or enabled in this service. CodeUnimplemented Code = 12 // CodeInternal indicates that some invariants expected by the underlying // system have been broken. This code is reserved for serious errors. CodeInternal Code = 13 // CodeUnavailable indicates that the service is currently unavailable. This // is usually temporary, so clients can back off and retry idempotent // operations. CodeUnavailable Code = 14 // CodeDataLoss indicates that the operation has resulted in unrecoverable // data loss or corruption. CodeDataLoss Code = 15 // CodeUnauthenticated indicates that the request does not have valid // authentication credentials for the operation. CodeUnauthenticated Code = 16 minCode = CodeCanceled maxCode = CodeUnauthenticated ) func (c Code) String() string { switch c { case CodeCanceled: return "canceled" case CodeUnknown: return "unknown" case CodeInvalidArgument: return "invalid_argument" case CodeDeadlineExceeded: return "deadline_exceeded" case CodeNotFound: return "not_found" case CodeAlreadyExists: return "already_exists" case CodePermissionDenied: return "permission_denied" case CodeResourceExhausted: return "resource_exhausted" case CodeFailedPrecondition: return "failed_precondition" case CodeAborted: return "aborted" case CodeOutOfRange: return "out_of_range" case CodeUnimplemented: return "unimplemented" case CodeInternal: return "internal" case CodeUnavailable: return "unavailable" case CodeDataLoss: return "data_loss" case CodeUnauthenticated: return "unauthenticated" } return fmt.Sprintf("code_%d", c) } // MarshalText implements [encoding.TextMarshaler]. func (c Code) MarshalText() ([]byte, error) { return []byte(c.String()), nil } // UnmarshalText implements [encoding.TextUnmarshaler]. func (c *Code) UnmarshalText(data []byte) error { dataStr := string(data) switch dataStr { case "canceled": *c = CodeCanceled return nil case "unknown": *c = CodeUnknown return nil case "invalid_argument": *c = CodeInvalidArgument return nil case "deadline_exceeded": *c = CodeDeadlineExceeded return nil case "not_found": *c = CodeNotFound return nil case "already_exists": *c = CodeAlreadyExists return nil case "permission_denied": *c = CodePermissionDenied return nil case "resource_exhausted": *c = CodeResourceExhausted return nil case "failed_precondition": *c = CodeFailedPrecondition return nil case "aborted": *c = CodeAborted return nil case "out_of_range": *c = CodeOutOfRange return nil case "unimplemented": *c = CodeUnimplemented return nil case "internal": *c = CodeInternal return nil case "unavailable": *c = CodeUnavailable return nil case "data_loss": *c = CodeDataLoss return nil case "unauthenticated": *c = CodeUnauthenticated return nil } // Ensure that non-canonical codes round-trip through MarshalText and // UnmarshalText. if after, ok := strings.CutPrefix(dataStr, "code_"); ok { dataStr = after code, err := strconv.ParseUint(dataStr, 10 /* base */, 32 /* bitsize */) if err == nil && (code < uint64(minCode) || code > uint64(maxCode)) { *c = Code(code) return nil } } return fmt.Errorf("invalid code %q", dataStr) } // CodeOf returns the error's status code if it is or wraps an [*Error] and // [CodeUnknown] otherwise. func CodeOf(err error) Code { if connectErr, ok := asError(err); ok { return connectErr.Code() } return CodeUnknown }