errs

package module
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2026 License: MIT Imports: 6 Imported by: 0

README

errs

Opinionated error primitives for Go HTTP services. Maps canonical errors to HTTP semantics, structured logging, and safe JSON responses.

Install

go get github.com/4nd3r5on/errs

Usage

Basic errors
// Canonical errors
return errs.ErrNotFound
return errs.ErrInvalidArgument

// Custom errors
return errs.New("database connection failed")
return errs.Newf("invalid user_id: %d", id)
Structured errors
// arguments of type errs.Option can modify error's internals
// but they don't affect the formatting.
// can be used with both errs.New and errs.Newf
err := errs.Newf("user %s not found", userID, func(err *Error) {
    err.SafeMessage = "User not found"
    err.UserDetails = map[string]any{"user_id": userID}
    err.LogDetails = []any{
        "attempted_id", userID,
        "query_duration_ms", 42,
    }
    err.Domain = "users"
})
return err
Factory API
// Explicit control over visibility and structure
return errs.F().
    Message("user query failed: %v", dbErr).
    UserMessage("User not found").
    Logs([]any{"user_id", id, "duration_ms", 42}).
    Mark(dbErr).  // infers public/private from marked error's HTTP code
    Domain("users").
    Err()

// Force visibility regardless of marked errors
return errs.F().
    Message("rate limit exceeded").
    Mark(errs.ErrRateLimited).
    Private().  // lock as private (500) despite 429 marker
    Err()

// Minimal usage
return errs.F().Message("db timeout").Mark(context.DeadlineExceeded).Err()

Visibility inference: Mark() auto-sets public if any marked error maps to <500. Override with Private()/Public().

Immutability: Each method returns a new factory instance. Safe to reuse base factories.

HTTP handling
func Handler(w http.ResponseWriter, r *http.Request) {
    user, err := getUser(ctx, id)
    if errs.HandleHTTP(ctx, w, r, err) {
        return // Error logged and JSON response sent
    }
    json.NewEncoder(w).Encode(user)
}

Response on error:

{
  "error": "User not found",
  "details": {"user_id": "123"}
}
Direct logging
errs.LogErr(ctx, err,
    errs.LogErrUseLogLevel(slog.LevelWarn),
    errs.LogErrUseLoggerAttrs("request_id", reqID),
)

Error mapping

Error HTTP Status
ErrNotFound 404
ErrInvalidArgument, ErrMissingArgument, ErrOutOfRange 400
ErrUnauthorized 401
ErrPermissionDenied 403
ErrExists, ErrOutdated 409
ErrRateLimited 429
ErrNotImplemented 501
ErrRemoteServiceErr 502
context.DeadlineExceeded 504
Others 500

Features

  • Standard library only
  • Error wrapping: Compatible with errors.Is/As and %w
  • Safe by default: Internal errors hidden unless ExposeInternal=true
  • Structured logging: Attach arbitrary data for logs and JSON responses separately
  • HTTP-aware: Automatic status code mapping and JSON rendering
  • Fluent factories: Declarative error construction with visibility inference

Documentation

Overview

Package errs provides opinionated error primitives and error handling

It defines a small set of canonical error values, maps them to HTTP semantics, and exposes helpers for rendering safe, structured error responses.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotImplemented   = errors.New("not implemented")
	ErrRemoteServiceErr = errors.New("remote service error")
	ErrRateLimited      = errors.New("rate limited")

	ErrInvalidArgument = errors.New("invalid argument")
	ErrMissingArgument = errors.New("missing argument")
	ErrOutOfRange      = errors.New("out of range")

	ErrPermissionDenied = errors.New("permission denied")
	ErrUnauthorized     = errors.New("unauthorized")

	ErrExists   = errors.New("already exists")
	ErrNotFound = errors.New("not found")
	ErrOutdated = errors.New("outdated")
)
View Source
var DefaultLogErrOptions = LogErrOptions{
	Logger:      slog.Default(),
	LogLevel:    slog.LevelError,
	LoggerAttrs: []any{},
}

Functions

func GetHTTPCode

func GetHTTPCode(err error) int

func HTTPGetLogLevel added in v0.0.7

func HTTPGetLogLevel(status int) slog.Level

func HandleHTTP added in v0.0.3

func HandleHTTP(
	ctx context.Context,
	w http.ResponseWriter,
	r *http.Request,
	err error,
	opts ...LogErrOption,
) (handled bool)

func IsAny added in v0.0.3

func IsAny(err error, references ...error) bool

func LogErr

func LogErr(ctx context.Context, err error, opts ...LogErrOption)

func Mark added in v0.0.5

func Mark(err error, marker error, opts ...Option) error

Mark marks an error with a sentinel error for errors.Is matching. Returns nil if err is nil. The original error message is preserved; marker is only for Is() matching.

func New added in v0.0.3

func New(internalMsg string, opts ...Option) error

New creates a new *Error

func Newf added in v0.0.3

func Newf(internalMsgFmt string, args ...any) error

Newf creates a new *Error with formatted internal message and optional wrapped error. Usage examples:

Newf("something failed: %w", err) // wraps err
Newf("simple error without wrapping")

func Wrap added in v0.0.5

func Wrap(err error, msg string, opts ...Option) error

Wrap wraps an error with additional context string. Returns nil if err is nil. Preserves original error for errors.Is/As.

Types

type Error added in v0.0.3

type Error struct {
	// Internal is the underlying cause.
	// By being an 'error' type, it allows for %w wrapping.
	Internal error

	// Whether or not show user external message if Message field is empty
	ExposeInternal bool

	// SafeMessage is the "Safe" human-readable message intended for the end-user.
	SafeMessage string

	// LogDetails contains data for slog.
	LogDetails []any

	// UserDetails gets marshaled to the JSON response and sent to the user
	UserDetails any

	// TraceID or Domain can be added here for "Marking" where the error originated.
	Domain string

	// Markers holds sentinel errors for errors.Is matching
	Markers []error
}

func (*Error) Error added in v0.0.3

func (e *Error) Error() string

Error implements the error interface. Returns Internal error message

func (*Error) Is added in v0.0.5

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

Is implements errors.Is matching for marked sentinel errors

func (*Error) Unwrap added in v0.0.3

func (e *Error) Unwrap() error

Unwrap returns the underlying wrapped error to support errors.As and errors.Is.

type ErrorHTTPResponse added in v0.0.3

type ErrorHTTPResponse struct {
	Error   string `json:"error"`
	Details any    `json:"details,omitempty"`
}

type Factory added in v0.0.6

type Factory interface {
	Message(fstr string, args ...any) Factory
	UserMessage(fstr string, args ...any) Factory
	Logs([]any) Factory
	Mark(...error) Factory
	Private() Factory
	Public() Factory
	Domain(string) Factory
	Err() error
}

func F added in v0.0.6

func F() Factory

type LogErrOption

type LogErrOption func(*LogErrOptions)

func LogErrUseLogLevel

func LogErrUseLogLevel(level slog.Level) LogErrOption

func LogErrUseLogger

func LogErrUseLogger(logger *slog.Logger) LogErrOption

func LogErrUseLoggerAttrs added in v0.0.3

func LogErrUseLoggerAttrs(args ...any) LogErrOption

type LogErrOptions

type LogErrOptions struct {
	Logger      *slog.Logger
	LogLevel    slog.Level
	LoggerAttrs []any
}

type Option added in v0.0.4

type Option func(*Error)

Option can be provided in args to New and Newf to change error's parameters

Jump to

Keyboard shortcuts

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