Documentation
¶
Overview ¶
Package errors provides a structured, transport-aware error model for the go-wind framework. It is the single source of truth for business errors that must flow across service boundaries (gRPC, HTTP) while remaining compatible with the Go standard library errors package.
Error model ¶
A WindError carries three pieces of information that together describe any business failure:
- Code — the transport category (a uint32 that maps 1:1 to gRPC codes.Code). It decides the HTTP status and gRPC status code the error is transported as. Declared as uint32 (not codes.Code) so this core package stays free of any gRPC dependency; the HTTP conversion is provided in-package by CodeToHTTP / HTTPToCode (returning plain int, no net/http dependency), and the gRPC conversion is done by callers at the transport boundary via codes.Code(wErr.Code). Generated error helpers may instead carry an explicit HTTP status (400–599) in Code when the declared status has no gRPC counterpart; see CodeToHTTP.
- Reason — a domain-unique, stable identifier such as "ORDER_NOT_FOUND". This is the value business code matches on with errors.Is; it must be treated as an immutable contract, never localized.
- Details — a map of dynamic variables (e.g. {"id": "42"}) consumed by the frontend for i18n substitution.
In addition, a WindError optionally wraps a cause (the underlying native error, e.g. a database driver error) and a captured call stack for log collectors.
Construction ¶
Prefer the category-specific factories over the generic New:
if order == nil {
return errors.NotFound("ORDER_NOT_FOUND")
}
The full set of factories: BadRequest, NotFound, AlreadyExists, PermissionDenied, ResourceExhausted, FailedPrecondition, Unauthenticated, Internal, Unimplemented, Unavailable, DeadlineExceeded. Each maps to a [Code] constant aligned with gRPC codes.Code; see types.go for the complete table.
Use New only when you need a Code outside the common factories.
Chainable builders and immutability ¶
WindError.WithDetails, WindError.WithCause, and WindError.WithCode return a new instance; the receiver is never mutated. This makes it safe to keep sentinel errors as package-level vars and derive specific instances at call sites:
var ErrOrderNotFound = errors.NotFound("ORDER_NOT_FOUND")
// Safe: returns a NEW instance, ErrOrderNotFound stays pristine.
return ErrOrderNotFound.WithDetails(map[string]string{"id": id})
HTTP mapping ¶
The package ships a built-in, zero-dependency mapping between Code and HTTP status codes, so HTTP servers do not need an external translation layer:
w.WriteHeader(errors.CodeToHTTP(wErr.Code))
CodeToHTTP returns the HTTP status for a Code (unknown codes fall back to 500, never a misleading 2xx). The mapping follows the canonical grpc-httpjson-transcoding / Envoy table and covers all 17 codes; explicit HTTP statuses carried in Code (400–599, set by generated error helpers) pass through unchanged.
HTTPToCode is the reverse direction. Because Code→HTTP is many-to-one (e.g. InvalidArgument, FailedPrecondition, and OutOfRange all map to 400), the reverse returns the single most representative Code; unmapped HTTP statuses fall back to CodeUnknown.
Both functions return plain int / uint32 — the errors package deliberately does not import net/http, mirroring the same layering rule that keeps Code decoupled from gRPC's codes.Code.
Standard library compatibility ¶
This package is named errors, which shadows the standard library. The most common helpers are re-exported as package-level functions so callers can import a single package: Is, As, Unwrap, Join. These bridge straight to the standard library and honor the WindError methods below.
WindError implements:
- WindError.Is — two WindErrors match when their Reason is equal. errors.Is walks the cause chain, so a wrapped WindError still matches its sentinel by Reason.
- WindError.Unwrap — returns the cause, enabling errors.As and errors.Is traversal.
For the typed convenience counterpart of As, use FromError to obtain a *WindError and a bool in one call, avoiding the two-line "var wErr *WindError; errors.As(...)" boilerplate.
Stack traces ¶
The chainable builders (WindError.WithDetails and WindError.WithCause) capture the call stack at the point they are invoked; WindError.StackTrace formats it for logging. A bare New does not capture a stack — capture happens lazily the first time a builder runs, and is idempotent across subsequent builders chained on the same instance.
Note: New is NOT re-exported as a bridge because the project signature New(code, reason) clashes with the standard library's New(string). Callers needing the standard library New should alias the import.
Index ¶
- Constants
- func As(err error, target any) bool
- func CodeToHTTP(code uint32) int
- func HTTPToCode(httpStatus int) uint32
- func Is(err, target error) bool
- func Join(errs ...error) error
- func Unwrap(err error) error
- type WindError
- func AlreadyExists(reason string) *WindError
- func BadRequest(reason string) *WindError
- func DeadlineExceeded(reason string) *WindError
- func FailedPrecondition(reason string) *WindError
- func FromError(err error) (*WindError, bool)
- func Internal(reason string) *WindError
- func New(code uint32, reason string) *WindError
- func NotFound(reason string) *WindError
- func PermissionDenied(reason string) *WindError
- func ResourceExhausted(reason string) *WindError
- func Unauthenticated(reason string) *WindError
- func Unavailable(reason string) *WindError
- func Unimplemented(reason string) *WindError
- func (e *WindError) Error() string
- func (e *WindError) Is(target error) bool
- func (e *WindError) StackTrace() string
- func (e *WindError) Unwrap() error
- func (e *WindError) WithCause(cause error) *WindError
- func (e *WindError) WithCode(code uint32) *WindError
- func (e *WindError) WithDetails(details map[string]string) *WindError
Constants ¶
const ( CodeOK uint32 = 0 // OK, success CodeCanceled uint32 = 1 // CANCELED, caller cancelled the call CodeUnknown uint32 = 2 // UNKNOWN, unknown error CodeInvalidArgument uint32 = 3 // INVALID_ARGUMENT, invalid argument CodeDeadlineExceeded uint32 = 4 // DEADLINE_EXCEEDED, deadline exceeded CodeNotFound uint32 = 5 // NOT_FOUND, resource not found CodeAlreadyExists uint32 = 6 // ALREADY_EXISTS, resource already exists CodePermissionDenied uint32 = 7 // PERMISSION_DENIED, permission denied CodeResourceExhausted uint32 = 8 // RESOURCE_EXHAUSTED, resource exhausted (rate limited) CodeFailedPrecondition uint32 = 9 // FAILED_PRECONDITION, precondition not met CodeAborted uint32 = 10 // ABORTED, transaction aborted (usually retryable) CodeOutOfRange uint32 = 11 // OUT_OF_RANGE, value out of range CodeUnimplemented uint32 = 12 // UNIMPLEMENTED, not implemented CodeInternal uint32 = 13 // INTERNAL, internal error CodeDataLoss uint32 = 15 // DATA_LOSS, data loss CodeUnauthenticated uint32 = 16 // UNAUTHENTICATED, not authenticated )
Code is the standard transport-level error category; its values map exactly to gRPC's codes.Code. By declaring Code as uint32 instead of depending on codes.Code directly, the errors core package stays free of any gRPC dependency. HTTP status conversion is provided in-package by CodeToHTTP / HTTPToCode (returning int, no net/http dependency); the gRPC conversion is done by callers at the transport boundary via codes.Code(wErr.Code). Besides the gRPC range (0–16), Code may carry an explicit HTTP status (400–599) set by generated error helpers; see CodeToHTTP.
Do NOT change these numeric values: they are kept in one-to-one correspondence with the gRPC protocol, the HTTP mapping table in http.go (see CodeToHTTP), and the errdetails.ErrorInfo semantics.
Variables ¶
This section is empty.
Functions ¶
func As ¶
As bridges the standard library errors.As: finds the first error in err's chain assignable to *target. Call sites that need to unwrap an underlying error into a *WindError rely on this function.
func CodeToHTTP ¶
CodeToHTTP returns the HTTP status code for the given Code. Two Code ranges are recognized:
- 0–16, the gRPC category range: mapped through the authoritative table above (grpc-httpjson-transcoding / Envoy / Google transcoding).
- 400–599, explicit HTTP statuses: generated error helpers (the protoc-gen-wind-errors output) carry the per-reason HTTP status declared in the proto (errors.code) annotation directly in Code, because many statuses (402, 405, 418, 598, …) have no gRPC counterpart to express them with. They pass through unchanged so the transport reproduces the declared status exactly.
Anything else falls back to 500 (Internal Server Error) so that a malformed or future-defined code never accidentally produces a misleading 2xx.
func HTTPToCode ¶
HTTPToCode returns the Code for the given HTTP status code. Because the Code→HTTP direction is many-to-one, the returned Code is the single most representative one for that status. Unmapped HTTP statuses (e.g. 418, 451) fall back to CodeUnknown so callers can detect that no precise mapping exists.
func Is ¶
Is bridges the standard library errors.Is: reports whether err matches target along the error chain (Unwrap).
Types ¶
type WindError ¶
type WindError struct {
Code uint32 `json:"-"` // transport category (maps to gRPC codes.Code)
Reason string `json:"reason"` // domain-unique identifier (e.g. "ORDER_NOT_FOUND")
Details map[string]string `json:"details"` // dynamic variables for i18n translation
// contains filtered or unexported fields
}
WindError implements the native Go error interface.
func AlreadyExists ¶
AlreadyExists creates a resource-already-exists error (ALREADY_EXISTS).
func BadRequest ¶
BadRequest creates an invalid-argument error (INVALID_ARGUMENT).
func DeadlineExceeded ¶
DeadlineExceeded creates a deadline-exceeded error (DEADLINE_EXCEEDED).
func FailedPrecondition ¶
FailedPrecondition creates a failed-precondition error (FAILED_PRECONDITION).
func FromError ¶
FromError extracts a *WindError from err's chain. Returns (nil, false) when err is nil or contains no WindError. It is the typed convenience counterpart of As: callers avoid the "var wErr *WindError; errors.As(...)" boilerplate.
func PermissionDenied ¶
PermissionDenied creates a permission-denied error (PERMISSION_DENIED).
func ResourceExhausted ¶
ResourceExhausted creates a resource-exhausted / rate-limited error (RESOURCE_EXHAUSTED).
func Unauthenticated ¶
Unauthenticated creates an unauthenticated error (UNAUTHENTICATED).
func Unavailable ¶
Unavailable creates a service-unavailable error (UNAVAILABLE).
func Unimplemented ¶
Unimplemented creates a not-implemented error (UNIMPLEMENTED).
func (*WindError) Is ¶
Is supports Go standard library errors.Is comparisons. Two WindErrors match when their Reason is equal; Code/Details/cause are intentionally ignored so business code can match on the stable domain identifier.
func (*WindError) StackTrace ¶
StackTrace formats the captured call stack as a string, for log collectors.
func (*WindError) WithCause ¶
WithCause is a chainable builder: wraps a native underlying error (e.g. a database error) and captures the current call stack.