Help us shape the future of Go! Take the 2026 Go Developer Survey

apperror

package
v0.3.0 Latest Latest
Warning

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

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

README

Application errors

apperror describes safe failures without depending on HTTP, gRPC, or a broker. Use one typed code and a public message. Validation errors may also contain field messages. Keep diagnostic causes in wrapped or joined errors, not public messages.

var ErrEmailTaken = apperror.Conflict("This email address is already registered.")

return apperror.InvalidFields(map[string][]string{
    "email": {"Use your work email.", "This address is not eligible."},
    "profile.name": {"Choose a display name."},
})

Use errors.Is(err, ErrEmailTaken) for sentinel identity and errors.As to inspect an application error through wrapping. Two separately constructed errors with the same code are not the same sentinel. Fields are copied on input and output so shared errors cannot be mutated by callers. Do not compare message text.

Transport adapters own translation. The application's HTTP adapter maps:

Code HTTP status
invalid_input 400
validation_failed 422
unauthenticated 401
forbidden 403
not_found 404
conflict 409
unavailable 503
rate_limited 429
internal_error, unknown, or empty 500 with a generic message

At the HTTP boundary, return response.Error(ctx, err) from the handler. Each field message becomes a Huma-style detail in the problem's errors array. Bare field names become body locations (email → body.email); nested dot/index paths are preserved, and already qualified HTTP locations such as query.page remain as supplied. Keys are sorted and message order is retained. Aggregate validation messages in one InvalidFields call, rather than joining separate field errors. See HTTP responses for the complete wire shape.

Request-body size errors remain HTTP-specific: 413 with request_too_large. Rate-limit timing and Retry-After headers belong to the rate limiter/HTTP boundary; a classification alone does not implement throttling. Future transports must supply their own mapping and must not expose internal or unknown error messages.

Migration from Kind and a separate code

Use Conflict(message) instead of Conflict("identity.email_taken", message) or New(KindConflict, "identity.email_taken", message). Use Code() instead of Kind(). Message-only constructors are available for every code: Internal, InvalidInput, Validation, Unauthenticated, Forbidden, NotFound, Conflict, Unavailable, and RateLimited. Validation(message) supplies a general validation message; InvalidFields(fields) includes field-level details. New(code, message) remains the low-level constructor; application callers should prefer the named shortcuts.

This changes the public JSON error code: identity.email_taken becomes conflict, identity.user_not_found becomes not_found, and malformed JSON/query errors become invalid_input. Validation retains validation_failed and its field-message arrays. Clients should branch on these codes and use field details for validation feedback. Domain-specific sentinel identity remains available to Go callers.

Documentation

Overview

Package apperror describes safe application failures without transport dependencies. Wrap errors with fmt.Errorf("operation: %w", err); use errors.Is/As to inspect them.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Code

type Code string

Code classifies a failure independently of HTTP or gRPC status codes. Transport adapters must treat unknown codes as internal failures.

const (
	CodeInternal        Code = "internal_error"
	CodeInvalidInput    Code = "invalid_input"
	CodeValidation      Code = "validation_failed"
	CodeUnauthenticated Code = "unauthenticated"
	CodeForbidden       Code = "forbidden"
	CodeNotFound        Code = "not_found"
	CodeConflict        Code = "conflict"
	CodeUnavailable     Code = "unavailable"
	CodeRateLimited     Code = "rate_limited"
)

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error carries an explicitly public code and message. Fields are private so package-level domain errors can be shared safely between concurrent callers. Error identity is pointer identity: errors.Is matches the original sentinel.

func Conflict

func Conflict(message string) *Error

Conflict creates an error with CodeConflict.

func Forbidden

func Forbidden(message string) *Error

Forbidden creates an error with CodeForbidden.

func Internal

func Internal(message string) *Error

Internal creates an error with CodeInternal.

func InvalidFields

func InvalidFields(fields map[string][]string) *Error

InvalidFields copies field errors. Keys are public input names, using dot paths for nested fields. Values are safe messages, never submitted values.

func InvalidInput

func InvalidInput(message string) *Error

InvalidInput creates an error with CodeInvalidInput.

func New

func New(code Code, message string) *Error

New creates an application error. Code and message must never contain secrets, raw database errors, or untrusted input. Internal errors are hidden by transports.

func NotFound

func NotFound(message string) *Error

NotFound creates an error with CodeNotFound.

func RateLimited

func RateLimited(message string) *Error

RateLimited creates an error with CodeRateLimited.

func Unauthenticated

func Unauthenticated(message string) *Error

Unauthenticated creates an error with CodeUnauthenticated.

func Unavailable

func Unavailable(message string) *Error

Unavailable creates an error with CodeUnavailable.

func Validation

func Validation(message string) *Error

Validation creates an error with CodeValidation.

func (*Error) Code

func (e *Error) Code() Code

func (*Error) Error

func (e *Error) Error() string

func (*Error) Fields

func (e *Error) Fields() map[string][]string

Fields returns an independent copy; callers cannot mutate shared error state.

func (*Error) Message

func (e *Error) Message() string

Jump to

Keyboard shortcuts

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