Documentation
¶
Overview ¶
Package xerrors provides structured application errors with stable typed codes, cause chaining, and key-value context fields.
Every error carries a machine-readable Code (gRPC-aligned wire value), a human-readable message, an optional cause, and optional structured fields. *Err implements errs.CodedError and errs.ContextualError from contracts, enabling logz to enrich log records automatically without importing this package.
Usage:
// Named constructors for common codes
err := xerrors.NotFound("user %s not found", userID)
err := xerrors.InvalidInput("email is required")
// Builder pattern for structured context
err := xerrors.New(xerrors.ErrInvalidInput, "validation failed").
WithContext("field", "email").
WithContext("rule", "required").
WithError(cause)
// Inspecting errors
var e *xerrors.Err
if errors.As(err, &e) {
switch e.Code() {
case xerrors.ErrNotFound:
// handle 404
}
}
Index ¶
- Constants
- type Code
- type Err
- func Aborted(msg string, args ...any) *Err
- func AlreadyExists(msg string, args ...any) *Err
- func Cancelled(msg string, args ...any) *Err
- func DataLoss(msg string, args ...any) *Err
- func DeadlineExceeded(msg string, args ...any) *Err
- func Gone(msg string, args ...any) *Err
- func Internal(msg string, args ...any) *Err
- func InvalidInput(msg string, args ...any) *Err
- func New(code Code, message string) *Err
- func NotFound(msg string, args ...any) *Err
- func NotImplemented(msg string, args ...any) *Err
- func OutOfRange(msg string, args ...any) *Err
- func PermissionDenied(msg string, args ...any) *Err
- func PreconditionFailed(msg string, args ...any) *Err
- func RateLimited(msg string, args ...any) *Err
- func Unauthorized(msg string, args ...any) *Err
- func Unavailable(msg string, args ...any) *Err
- func Wrap(code Code, message string, err error) *Err
- func (e *Err) Code() Code
- func (e *Err) Detailed() string
- func (e *Err) Error() string
- func (e *Err) ErrorCode() string
- func (e *Err) ErrorContext() map[string]any
- func (e *Err) Fields() map[string]any
- func (e *Err) Issues() []Issue
- func (e *Err) MarshalJSON() ([]byte, error)
- func (e *Err) Message() string
- func (e *Err) PlatformCode() string
- func (e *Err) Problem(typeBase string) Problem
- func (e *Err) Unwrap() error
- func (e *Err) WithContext(key string, value any) *Err
- func (e *Err) WithError(err error) *Err
- func (e *Err) WithIssue(pointer, detail string) *Err
- func (e *Err) WithPlatformCode(code string) *Err
- type Issue
- type Problem
Constants ¶
const DefaultTypeBase = "urn:einherjar:error:"
DefaultTypeBase prefixes problem type URIs when an application configures none.
A URN rather than an https URL: RFC 9457 allows a non-resolvable type URI and says consumers "SHOULD NOT automatically dereference" it, so nothing has to be hosted for the identifier to be valid. An application that wants to publish documentation configures an https base instead.
Applications are expected to replace this. A client of an application's API couples to the type URI, and it should identify that application's problem types rather than announcing which framework built it.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Code ¶
type Code string
Code is the machine-readable error category. Wire values are stable across versions and are identical to gRPC status code names. HTTP mapping is the responsibility of the transport layer, not this package.
const ( // ErrInvalidInput indicates the request contains malformed or invalid data. // HTTP 400 / gRPC INVALID_ARGUMENT. ErrInvalidInput Code = "INVALID_ARGUMENT" // ErrOutOfRange indicates a parameter or index exceeds the valid bounds. // Use when the value itself is well-formed but falls outside accepted limits // (e.g. page number past the last page, offset past end of file). // HTTP 400 / gRPC OUT_OF_RANGE. ErrOutOfRange Code = "OUT_OF_RANGE" // HTTP 401 / gRPC UNAUTHENTICATED. ErrUnauthorized Code = "UNAUTHENTICATED" // ErrPermissionDenied indicates the authenticated caller lacks permission for // the operation. Authentication is not the issue. // HTTP 403 / gRPC PERMISSION_DENIED. ErrPermissionDenied Code = "PERMISSION_DENIED" // ErrNotFound indicates the requested resource does not exist. // HTTP 404 / gRPC NOT_FOUND. ErrNotFound Code = "NOT_FOUND" // ErrAlreadyExists indicates a resource with the same identifier already exists. // Use for creation conflicts (e.g. duplicate email on sign-up). // HTTP 409 / gRPC ALREADY_EXISTS. ErrAlreadyExists Code = "ALREADY_EXISTS" // ErrAborted indicates the operation was aborted due to a concurrent modification // or transaction conflict. Unlike ErrAlreadyExists (creation conflict) and // ErrPreconditionFailed (business rule), this signals the caller may retry. // HTTP 409 / gRPC ABORTED. ErrAborted Code = "ABORTED" // ErrGone indicates the resource existed but has been permanently removed. // Unlike ErrNotFound, this signals the caller should not retry. // HTTP 410 / non-standard gRPC extension. ErrGone Code = "GONE" // ErrPreconditionFailed indicates a required business condition was not met. // The input is valid but a rule blocks the action (e.g. "cannot delete an // account with active subscriptions"). // HTTP 412 / gRPC FAILED_PRECONDITION. ErrPreconditionFailed Code = "FAILED_PRECONDITION" // ErrRateLimited indicates the caller has exceeded a rate limit or quota. // HTTP 429 / gRPC RESOURCE_EXHAUSTED. ErrRateLimited Code = "RESOURCE_EXHAUSTED" // ErrCancelled indicates the operation was cancelled by the caller. // HTTP 499 / gRPC CANCELLED. ErrCancelled Code = "CANCELLED" // ErrInternal indicates an unexpected server-side failure. // Use a more specific code when one applies. // HTTP 500 / gRPC INTERNAL. ErrInternal Code = "INTERNAL" // ErrDataLoss indicates unrecoverable data loss or corruption. // Reserved for storage-layer integrity failures. // HTTP 500 / gRPC DATA_LOSS. ErrDataLoss Code = "DATA_LOSS" // ErrNotImplemented indicates the requested operation has not been implemented. // HTTP 501 / gRPC UNIMPLEMENTED. ErrNotImplemented Code = "UNIMPLEMENTED" // HTTP 503 / gRPC UNAVAILABLE. ErrUnavailable Code = "UNAVAILABLE" // ErrDeadlineExceeded indicates the operation timed out before completing. // HTTP 504 / gRPC DEADLINE_EXCEEDED. ErrDeadlineExceeded Code = "DEADLINE_EXCEEDED" )
func (Code) Description ¶
Description returns a human-readable description for the code. Unknown codes return their raw string value.
func (Code) Slug ¶ added in v1.14.0
Slug renders the code as a URI path segment: the stable wire value lowercased with underscores turned into hyphens, so INVALID_ARGUMENT becomes invalid-argument. It is the second half of a problem type URI, the first being the application's configured base.
Deriving it means all sixteen codes are covered with no per-code configuration, and a new code gets a type URI for free.
type Err ¶
type Err struct {
// contains filtered or unexported fields
}
Err is a structured application error carrying a Code, a human-readable message, an optional cause, and optional key-value context fields.
It implements the standard error interface, errors.Unwrap for cause chaining, and json.Marshaler for API responses. It also satisfies errs.CodedError and errs.ContextualError from contracts, enabling logz to enrich log records automatically without importing this package.
Use the builder methods WithContext, WithError, and WithPlatformCode to attach additional information after construction:
err := xerrors.New(xerrors.ErrInvalidInput, "validation failed").
WithContext("field", "email").
WithContext("rule", "required").
WithError(cause)
func AlreadyExists ¶
AlreadyExists creates an Err with ErrAlreadyExists code.
func DeadlineExceeded ¶
DeadlineExceeded creates an Err with ErrDeadlineExceeded code.
func InvalidInput ¶
InvalidInput creates an Err with ErrInvalidInput code.
func NotImplemented ¶
NotImplemented creates an Err with ErrNotImplemented code.
func OutOfRange ¶
OutOfRange creates an Err with ErrOutOfRange code.
func PermissionDenied ¶
PermissionDenied creates an Err with ErrPermissionDenied code.
func PreconditionFailed ¶
PreconditionFailed creates an Err with ErrPreconditionFailed code.
func RateLimited ¶
RateLimited creates an Err with ErrRateLimited code.
func Unauthorized ¶
Unauthorized creates an Err with ErrUnauthorized code.
func Unavailable ¶
Unavailable creates an Err with ErrUnavailable code.
func Wrap ¶
Wrap creates an Err that wraps an existing error with a code and message. The wrapped error is accessible via errors.Is, errors.As, and Err.Unwrap.
func (*Err) Detailed ¶
Detailed returns a verbose string useful for debugging. Format: "code: X | message: Y | cause: Z | fields: {...}"
func (*Err) Error ¶
Error implements the error interface. Format: "INVALID_ARGUMENT: username is required → original cause"
func (*Err) ErrorCode ¶
ErrorCode satisfies errs.CodedError. logz calls this to append error_code to log records without importing this package.
func (*Err) ErrorContext ¶
ErrorContext satisfies errs.ContextualError. logz calls this to append context fields to log records. The returned map is read-only by logz; use Fields() if you need a safe copy.
func (*Err) Fields ¶
Fields returns a shallow copy of the context fields. Returns an empty (non-nil) map if no fields have been set.
func (*Err) MarshalJSON ¶
MarshalJSON implements json.Marshaler, emitting the RFC 9457 problem shape with DefaultTypeBase and without the transport-filled members.
It agrees with what the HTTP layer sends apart from Status and Instance, which need a request. Before v1.14.0 it emitted a third shape that nothing in the framework used and that had drifted from the one clients actually received — two implementations of the same body, only one of them reachable. Output: {"code":"NOT_FOUND","platform_code":"...","message":"...","fields":{...}} platform_code and fields are omitted when empty.
func (*Err) PlatformCode ¶
PlatformCode returns the platform-level error code, or "" if none was set.
func (*Err) Problem ¶ added in v1.14.0
Problem renders the error as an RFC 9457 problem detail, with the type URI built from typeBase and the code's slug. An empty typeBase falls back to DefaultTypeBase.
Status and Instance are left zero: see Problem.
Fields are deliberately not copied in. They are the operator's log context, not the caller's business — the two used to share one map, which meant anything added for debugging was also published. Client-facing causes go in Errors.
func (*Err) Unwrap ¶
Unwrap returns the underlying cause, enabling errors.Is and errors.As to walk the full cause chain.
func (*Err) WithContext ¶
WithContext adds a key-value pair to the error's context fields and returns the receiver for chaining. Calling it multiple times with the same key overwrites the previous value.
func (*Err) WithIssue ¶ added in v1.14.0
WithIssue records one cause of the error, to be reported to the caller in the problem's errors array. Repeat it to report several.
pointer is a JSON Pointer into the request body with the "#" prefix RFC 9457 uses — "#/age". Pass an empty pointer when the cause is not a location in the body.
This is the client-facing counterpart to Err.WithContext: an issue is for whoever made the request, a context field is for whoever reads the logs.
func (*Err) WithPlatformCode ¶
WithPlatformCode sets a platform-level error code and returns the receiver for chaining. Platform codes are domain-specific identifiers (e.g. "EMPLOYEE_NOT_FOUND") intended for consuming applications — such as a frontend — that need to map errors to localised user-facing messages.
Platform codes are optional. Errors that have no user-actionable meaning (e.g. 500 internal errors) should not carry one.
type Issue ¶ added in v1.14.0
Issue is one entry of a problem's errors array: a single thing wrong with a request, optionally located within it.
It exists because one failed request often has more than one cause. Reporting only the first forces a caller into a fix-resubmit-discover loop, one round trip per mistake, which is the behaviour this type replaces.
Pointer is a JSON Pointer identifying where in the request body the problem is, carrying the "#" prefix that RFC 9457 writes in its own example — "#/age", "#/profile/color". It is empty when the cause is not a location in the body: a rule spanning two fields, or a conflict with state the body cannot express.
type Problem ¶ added in v1.14.0
type Problem struct {
// Type identifies the kind of problem, and is what a client switches on
// instead of parsing Detail. It is a URI reference, not necessarily a URL.
Type string `json:"type"`
// Title summarises the type and does not change between occurrences of it.
Title string `json:"title"`
// Status mirrors the HTTP status code. RFC 9457 makes it advisory and requires
// it to equal the status actually sent. Filled by the transport.
Status int `json:"status,omitempty"`
// Detail explains this occurrence, and is the member that varies per request.
Detail string `json:"detail,omitempty"`
// Instance identifies this occurrence. Filled by the transport.
Instance string `json:"instance,omitempty"`
// Code is the stable gRPC-aligned code, kept so that callers written against
// it keep working and so the 16-code set stays addressable.
Code string `json:"code,omitempty"`
// PlatformCode is the application's own catalogue code, when it sets one.
PlatformCode string `json:"platform_code,omitempty"`
// Errors lists every cause, when there is more than one or when a cause can be
// located in the request body.
Errors []Issue `json:"errors,omitempty"`
}
Problem is an RFC 9457 problem detail — the standard shape for an HTTP error response, served as "application/problem+json".
Two members are deliberately left for the transport to fill, because they are the only two this package cannot know:
- Status is the HTTP status code. Mapping a Code to a status belongs to the transport layer, which is Code's long-standing contract.
- Instance identifies this one occurrence, which in practice is the request ID — and there is no request here.
Everything else comes from the Err. Build one with Err.Problem.
Code, PlatformCode and Errors are extension members. RFC 9457 provides for them ("problem type definitions MAY extend the problem details object") and requires consumers to ignore extensions they do not recognise, which is what makes it safe to add more later.