errs

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package errs provides a single, transport-agnostic error type with stable codes, an HTTP (and later gRPC) status mapping, request validation helpers, and automatic redaction of secrets in user-facing messages.

An *Error carries a machine-readable Code, an optional Title, a human Message (the "detail"), and optional Args used for i18n. It implements the standard error interface and unwraps to any underlying cause, so it composes with errors.Is/errors.As. The JSON shape is small and stable ({"code","title","detail","fields"}) so an *Error can be returned directly to API clients.

Codes

Code is a stable classification whose integer values are aligned with google.golang.org/grpc/codes, so gRPC mapping is an identity and HTTP mapping is a small lookup table. Each code has a snake_case String (e.g. "invalid_argument") used as the wire "code" and as the default i18n key, and an HTTPStatus. The canonical codes are OK, Canceled, Unknown, InvalidArgument, DeadlineExceeded, NotFound, AlreadyExists, PermissionDenied, ResourceExhausted, FailedPrecondition, Aborted, OutOfRange, Unimplemented, Internal, Unavailable, DataLoss, and Unauthenticated.

Creating errors

New wraps an existing error, Newf formats a fresh message, and Wrapf wraps a cause behind a formatted prefix. The call site (function and file:line) is captured for logging. Chain WithTitle, WithMessageID, and WithArgs to enrich an error:

if w == nil {
    return errs.Newf(errs.NotFound, "widget %d not found", id).
        WithTitle("widget not found").
        WithMessageID("widget.not_found").
        WithArgs(map[string]any{"id": id})
}

// Wrap a low-level cause behind a stable code:
if err := db.QueryRow(...); err != nil {
    return errs.Wrapf(errs.Internal, err, "load widget %d", id)
}

Normalizing and inspecting

From normalizes any error into an *Error (an unrecognized error becomes Internal), and Is reports whether an error carries a given Code:

e := errs.From(err)                 // always an *errs.Error (or nil)
if errs.Is(err, errs.NotFound) {    // code-aware check
    ...
}

Encoding (HTTP)

Encode renders the error as JSON and returns (data, contentType, error), satisfying the web encoder contract. It runs the detail through Sanitize, so secrets never leak to clients. HTTPStatus gives the response status:

body, contentType, _ := e.Encode()
w.Header().Set("Content-Type", contentType)
w.WriteHeader(e.HTTPStatus())
w.Write(body)

Validation

Check validates a struct via github.com/go-playground/validator tags and, on failure, returns an InvalidArgument *Error whose Fields list the offending inputs. NewFieldErrors builds the same shape from an explicit list:

type CreateReq struct {
    Name  string `validate:"required"`
    Email string `validate:"required,email"`
}
if err := errs.Check(req); err != nil {
    return err // 400 with per-field FieldError entries
}

Sanitization

Sanitize redacts common secret patterns (password/token/secret/api_key in key=value and JSON forms) and truncates to MaxMessageLen on a UTF-8 boundary. Encode applies it automatically; call it directly for any other string bound for logs or responses.

i18n

An *Error is localized by the i18n package using MessageID (or, when unset, CodeStr) as the lookup key and Args as template data; see that package's TranslateError.

Index

Constants

View Source
const MaxMessageLen = 1024

MaxMessageLen bounds the length of a sanitized message to keep logs and API responses tidy.

Variables

This section is empty.

Functions

func Check

func Check(val any) error

Check validates val using struct tags (github.com/go-playground/validator). On failure it returns an *Error with code InvalidArgument whose Fields list the offending fields. On success it returns nil.

func Is

func Is(err error, code Code) bool

Is reports whether err is (or wraps) an *Error with the given code.

func Sanitize

func Sanitize(msg string) string

Sanitize redacts common secret patterns and truncates the message to MaxMessageLen on a UTF-8 boundary. It is safe to call on any string.

Types

type Code

type Code uint32

Code is a stable, transport-agnostic error classification. Its integer values are intentionally aligned with google.golang.org/grpc/codes so that gRPC mapping is an identity and HTTP mapping is a small lookup table.

const (
	OK                 Code = 0
	Canceled           Code = 1
	Unknown            Code = 2
	InvalidArgument    Code = 3
	DeadlineExceeded   Code = 4
	NotFound           Code = 5
	AlreadyExists      Code = 6
	PermissionDenied   Code = 7
	ResourceExhausted  Code = 8
	FailedPrecondition Code = 9
	Aborted            Code = 10
	OutOfRange         Code = 11
	Unimplemented      Code = 12
	Internal           Code = 13
	Unavailable        Code = 14
	DataLoss           Code = 15
	Unauthenticated    Code = 16
)

Canonical error codes, value-aligned with gRPC status codes.

func (Code) HTTPStatus

func (c Code) HTTPStatus() int

HTTPStatus maps the code to an HTTP status code.

func (Code) String

func (c Code) String() string

String returns the snake_case name of the code (e.g. "invalid_argument").

type Error

type Error struct {
	Code    Code           `json:"-"`
	CodeStr string         `json:"code"`
	Title   string         `json:"title,omitempty"`
	Message string         `json:"detail"`
	Fields  []FieldError   `json:"fields,omitempty"`
	Args    map[string]any `json:"-"`

	// MessageID is an optional i18n message key. When set it overrides CodeStr as
	// the lookup key the i18n package uses to localize Message; Args supply the
	// template data. Left empty, translation falls back to CodeStr.
	MessageID string `json:"-"`
	// contains filtered or unexported fields
}

Error is the canonical application error.

The JSON shape is intentionally small and stable so it can be returned directly to API clients:

{"code":"not_found","title":"...","detail":"widget 42 not found","fields":[...]}

func From

func From(err error) *Error

From normalizes any error into an *Error. If err already is (or wraps) an *Error it is returned as-is; otherwise it becomes an Internal error.

func New

func New(code Code, err error) *Error

New creates an *Error with the given code, wrapping err. The message is taken from err. The call site (function and file:line) is captured for logging.

func NewFieldErrors

func NewFieldErrors(msg string, fields ...FieldError) *Error

NewFieldErrors builds an InvalidArgument error from an explicit field list.

func Newf

func Newf(code Code, format string, args ...any) *Error

Newf creates an *Error with a formatted message and no wrapped cause.

func Wrapf

func Wrapf(code Code, err error, format string, args ...any) *Error

Wrapf wraps err with a code and a formatted message that prefixes the cause.

func (*Error) Encode

func (e *Error) Encode() ([]byte, string, error)

Encode renders the error as JSON, redacting any secrets in the detail. It satisfies the web encoder contract: (data, contentType, error).

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) HTTPStatus

func (e *Error) HTTPStatus() int

HTTPStatus returns the HTTP status code for this error.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the wrapped cause for errors.Is/errors.As.

func (*Error) WithArgs

func (e *Error) WithArgs(args map[string]any) *Error

WithArgs attaches i18n arguments and returns the error for chaining.

func (*Error) WithMessageID

func (e *Error) WithMessageID(id string) *Error

WithMessageID sets the i18n lookup key and returns the error for chaining.

func (*Error) WithTitle

func (e *Error) WithTitle(title string) *Error

WithTitle sets a human-friendly title and returns the error for chaining.

type FieldError

type FieldError struct {
	Field string `json:"field"`
	Error string `json:"error"`
}

FieldError describes a single failed validation constraint on an input field.

Jump to

Keyboard shortcuts

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