errors

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: MIT Imports: 5 Imported by: 0

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:

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

View Source
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
	CodeUnavailable        uint32 = 14 // UNAVAILABLE, service unavailable (retryable)
	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

func As(err error, target any) bool

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

func CodeToHTTP(code uint32) int

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

func HTTPToCode(httpStatus int) uint32

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

func Is(err, target error) bool

Is bridges the standard library errors.Is: reports whether err matches target along the error chain (Unwrap).

func Join

func Join(errs ...error) error

Join bridges the standard library errors.Join: merges multiple errors into one.

func Unwrap

func Unwrap(err error) error

Unwrap bridges the standard library errors.Unwrap: returns the immediate cause of err.

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

func AlreadyExists(reason string) *WindError

AlreadyExists creates a resource-already-exists error (ALREADY_EXISTS).

func BadRequest

func BadRequest(reason string) *WindError

BadRequest creates an invalid-argument error (INVALID_ARGUMENT).

func DeadlineExceeded

func DeadlineExceeded(reason string) *WindError

DeadlineExceeded creates a deadline-exceeded error (DEADLINE_EXCEEDED).

func FailedPrecondition

func FailedPrecondition(reason string) *WindError

FailedPrecondition creates a failed-precondition error (FAILED_PRECONDITION).

func FromError

func FromError(err error) (*WindError, bool)

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 Internal

func Internal(reason string) *WindError

Internal creates an internal error (INTERNAL).

func New

func New(code uint32, reason string) *WindError

func NotFound

func NotFound(reason string) *WindError

NotFound creates a resource-not-found error (NOT_FOUND).

func PermissionDenied

func PermissionDenied(reason string) *WindError

PermissionDenied creates a permission-denied error (PERMISSION_DENIED).

func ResourceExhausted

func ResourceExhausted(reason string) *WindError

ResourceExhausted creates a resource-exhausted / rate-limited error (RESOURCE_EXHAUSTED).

func Unauthenticated

func Unauthenticated(reason string) *WindError

Unauthenticated creates an unauthenticated error (UNAUTHENTICATED).

func Unavailable

func Unavailable(reason string) *WindError

Unavailable creates a service-unavailable error (UNAVAILABLE).

func Unimplemented

func Unimplemented(reason string) *WindError

Unimplemented creates a not-implemented error (UNIMPLEMENTED).

func (*WindError) Error

func (e *WindError) Error() string

Error implements the native error interface; tuned for log readability.

func (*WindError) Is

func (e *WindError) Is(target error) bool

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

func (e *WindError) StackTrace() string

StackTrace formats the captured call stack as a string, for log collectors.

func (*WindError) Unwrap

func (e *WindError) Unwrap() error

Unwrap supports Go standard library errors.Unwrap.

func (*WindError) WithCause

func (e *WindError) WithCause(cause error) *WindError

WithCause is a chainable builder: wraps a native underlying error (e.g. a database error) and captures the current call stack.

func (*WindError) WithCode

func (e *WindError) WithCode(code uint32) *WindError

WithCode is an escape hatch: allows overriding the transport category in special business scenarios.

func (*WindError) WithDetails

func (e *WindError) WithDetails(details map[string]string) *WindError

WithDetails is a chainable builder: injects the i18n variables needed by the frontend and captures the current call stack.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL