erm

package
v1.0.41 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2026 License: MPL-2.0 Imports: 7 Imported by: 0

README

ERM - Enhanced Error Management for Go

A comprehensive error management package for Go applications following clean architecture patterns. ERM enriches errors with stack traces, HTTP status codes, safe user-facing messages, validation error capabilities, and error collection support, while providing lightweight internationalization through the custom c3p0-box/utils/i18n package.

Features

  • Enhanced Errors: Stack traces (for 500 errors), HTTP status codes
  • Performance Optimized: Stack traces only captured for Internal Server Errors (500) where debugging is needed
  • Validation Support: Unified validation errors with field-level granularity
  • Error Collection: Collect multiple related errors with automatic flattening
  • Lightweight i18n: Uses the custom github.com/c3p0-box/utils/i18n package for internationalization
  • On-Demand Localization: Messages resolved when Error() or ErrMap() called
  • Per-Language Localizers: Automatic creation and caching of localizers for different languages
  • Clean Architecture: Interface-based design for testability and flexibility
  • Memory Efficient: Immutable error objects safe for concurrent access

Installation

go get github.com/c3p0-box/utils/erm

Quick Start

Basic Error Creation
import "github.com/c3p0-box/utils/erm"

// Create client errors (no stack trace for performance)
err := erm.New(http.StatusBadRequest, "Invalid email", originalErr)
err := erm.BadRequest("Invalid input", originalErr)

// Create server errors (with stack trace for debugging)
err := erm.New(http.StatusInternalServerError, "Database connection failed", dbErr)
err := erm.Internal("Database connection failed", dbErr)

// Create errors without underlying error (err parameter can be nil)
err := erm.New(http.StatusBadRequest, "Custom validation message", nil)
// err.Unwrap() returns nil, but err.Error() returns "Custom validation message"
Stack Trace Behavior
// Client errors (4xx) - no stack trace for performance
clientErr := erm.BadRequest("Invalid input", nil)
fmt.Println(len(clientErr.Stack()) == 0) // true - no stack trace

// Server errors (500) - stack trace captured for debugging
serverErr := erm.Internal("Database error", dbErr)
fmt.Println(len(serverErr.Stack()) > 0) // true - stack trace available

// Zero code defaults to 500 and captures stack trace
defaultErr := erm.New(0, "Something went wrong", nil)
fmt.Println(len(defaultErr.Stack()) > 0) // true - becomes 500 error
Validation Errors with i18n
// Create validation errors with message keys for i18n
err := erm.NewValidationError("validation.required", "email", "")
err = err.WithParam("min", 5)

// Use convenience constructors
err := erm.RequiredError("email", "")
err := erm.MinLengthError("password", "123", 8)
err := erm.EmailError("email", "invalid-email")
err := erm.DuplicateError("email", "user@example.com")
err := erm.InvalidError("format", "invalid-data")
err := erm.NotFound("user", dbErr) // 404 error with message key "error.not_found"
Field Localization

ERM supports localizing field names in error messages using i18n message keys, allowing for user-friendly, localized field names:

// Standard approach - field names not localized
err := erm.RequiredError("email", "")
fmt.Println(err.Error()) // "email is required"

// Field localization using message keys as optional parameter
err := erm.RequiredError("email", "", "fields.email")
// If "fields.email" translates to "Email Address", displays: "Email Address is required"

// Other localized field constructors
err := erm.MinLengthError("password", "123", 8, "fields.password")
err := erm.EmailError("email", "invalid@email", "fields.email")
err := erm.DuplicateError("username", "john", "fields.username")

// Manual field localization
err := erm.NewValidationError("validation.required", "user_email", "", "fields.user_email")

// Add field localization to existing errors using WithFieldMessageKey
err := erm.RequiredError("email", "").WithFieldMessageKey("fields.email")
Setting Up Field Translations

Define field name translations in your message files:

locales/en.json

{
  "fields.email": {
    "other": "Email Address"
  },
  "fields.password": {
    "other": "Password"
  },
  "fields.username": {
    "other": "Username"
  },
  "fields.first_name": {
    "other": "First Name"
  }
}

locales/es.json

{
  "fields.email": {
    "other": "Dirección de Email"
  },
  "fields.password": {
    "other": "Contraseña"
  },
  "fields.username": {
    "other": "Nombre de Usuario"
  },
  "fields.first_name": {
    "other": "Nombre"
  }
}
Internationalization Setup
import (
    "github.com/c3p0-box/utils/i18n"
    "golang.org/x/text/language"
)

// Get localizers for different languages (created automatically)
englishLocalizer := erm.GetLocalizer(language.English)
spanishLocalizer := erm.GetLocalizer(language.Spanish)

// For advanced usage with custom message files:
i18n.SetDefaultLanguage(language.English)

// Add translations programmatically
i18n.AddTranslation(language.English, "validation.required", "{{.field}} is required", "")
i18n.AddTranslation(language.Spanish, "validation.required", "{{.field}} es requerido", "")
Message File Format

Create translations in JSON format compatible with the custom i18n package:

locales/en.json

{
  "validation.required": {
    "other": "{{.field}} is required"
  },
  "validation.min_length": {
    "other": "{{.field}} must be at least {{.min}} characters long"
  },
  "validation.email": {
    "other": "{{.field}} must be a valid email address"
  },
  "error.not_found": {
    "other": "{{.field}} not found"
  }
}

locales/es.json

{
  "validation.required": {
    "other": "{{.field}} es requerido"
  },
  "validation.min_length": {
    "other": "{{.field}} debe tener al menos {{.min}} caracteres"
  },
  "validation.email": {
    "other": "{{.field}} debe ser una dirección de email válida"
  },
  "error.not_found": {
    "other": "{{.field}} no encontrado"
  }
}
On-Demand Localization
// Default localization (uses English by default)
err := erm.RequiredError("email", "")
fmt.Println(err.Error()) // "email is required"

// Custom localization with different languages
localizedMsg := err.LocalizedError(language.Spanish)
fmt.Println(localizedMsg) // Currently returns English since Spanish bundle uses English fallback

// For custom languages, extend the GetLocalizer system with your own bundles
// The system will automatically use English fallback for any language
spanishMsg := err.LocalizedError(language.Spanish)
fmt.Println(spanishMsg) // "email is required" (English fallback)

// Structured errors for APIs
errorMap := err.LocalizedErrMap(language.Spanish)
// Returns: {"email": ["email is required"]}

// Convenience method using default English localizer
errorMap = err.ErrMap()
// Returns: {"email": ["email is required"]}
Error Collection
// Collect multiple validation errors one by one
container := erm.New(http.StatusBadRequest, "Validation errors", nil)

err1 := erm.RequiredError("email", "")
err2 := erm.MinLengthError("password", "123", 8)

container.AddError(err1)
container.AddError(err2)

// Or collect multiple errors at once using AddErrors
container2 := erm.New(http.StatusBadRequest, "Validation errors", nil)
errors := []erm.Error{
    erm.RequiredError("email", ""),
    erm.MinLengthError("password", "123", 8),
    erm.EmailError("email", "invalid-email"),
}
container2.AddErrors(errors)

// Get localized error map for API responses
errorMap := container.ErrMap() // Uses English
// Or with specific language
errorMap = container.LocalizedErrMap(language.Spanish)

// Format: {"email": ["email is required"], "password": ["password must be at least 8 characters long"]}

// With field localization using optional field message key parameter
containerWithFields := erm.New(http.StatusBadRequest, "Validation errors", nil)
containerWithFields.AddError(erm.RequiredError("email", "", "fields.email"))
containerWithFields.AddError(erm.MinLengthError("password", "123", 8, "fields.password"))

localizedMap := containerWithFields.LocalizedErrMap(language.Spanish)
// Format: {"email": ["Dirección de Email es requerido"], "password": ["Contraseña debe tener al menos 8 caracteres"]}

Migration from SetLocalizer System

The old SetLocalizer/GetDefaultLocalizer API has been replaced with GetLocalizer(language.Tag):

Before:
// Old API (deprecated)
bundle := i18n.NewBundle(language.English)
// ... add messages to bundle ...
localizer := i18n.NewLocalizer(bundle, "en")
erm.SetLocalizer(localizer)
After:
// New API
englishLocalizer := erm.GetLocalizer(language.English)
spanishLocalizer := erm.GetLocalizer(language.Spanish)

API Reference

Core Functions
  • New(code int, msg string, err error) Error - Create enriched error (stack traces only for 500 errors)
  • GetLocalizer(tag language.Tag) *i18n.Localizer - Get or create localizer for language
Validation Constructors

All validation constructors now support optional field message keys for localization:

  • NewValidationError(messageKey, fieldName string, value interface{}, fieldMessageKey ...string) Error
  • RequiredError(fieldName string, value interface{}, fieldMessageKey ...string) Error
  • MinLengthError(fieldName string, value interface{}, min int, fieldMessageKey ...string) Error
  • MaxLengthError(fieldName string, value interface{}, max int, fieldMessageKey ...string) Error
  • EmailError(fieldName string, value interface{}, fieldMessageKey ...string) Error
  • MinValueError(fieldName string, value interface{}, min interface{}, fieldMessageKey ...string) Error
  • MaxValueError(fieldName string, value interface{}, max interface{}, fieldMessageKey ...string) Error
  • DuplicateError(fieldName string, value interface{}, fieldMessageKey ...string) Error
  • InvalidError(fieldName string, value interface{}, fieldMessageKey ...string) Error
  • NotFound(fieldName string, err error) Error - Creates 404 error with "error.not_found" message key
Error Interface
type Error interface {
    Error() string                              // Localized with English
    LocalizedError(language.Tag) string        // Localized with specific language
    LocalizedErrMap(language.Tag) map[string][]string
    
    MessageKey() string                         // i18n message key
    FieldName() string                          // Field name for validation
    FieldMessageKey() string                    // i18n message key for field name
    Value() interface{}
    Params() map[string]interface{}
    
    WithFieldMessageKey(string) Error           // Set field message key for localization
    
    AddError(Error)                             // Error collection (mutable)
    AddErrors([]Error)                          // Batch error collection (mutable)
    ErrMap() map[string][]string                // Structured errors (uses English localizer)
    // ... other methods
}

VIX Integration

ERM powers VIX validation. See VIX documentation for validation usage.

Best Practices

  1. Use per-language localizers: Get localizers using erm.GetLocalizer(language.Tag) for different languages
  2. Use message keys: Prefer message keys over hardcoded templates for better maintainability
  3. Organize message files: Group related validations and use consistent key naming
  4. Error collection: Use error collection for batch validation scenarios
  5. Structured output: Use ErrMap() for API error responses
  6. Performance-aware error codes: Use appropriate HTTP status codes - stack traces are only captured for 500 errors to optimize performance for client errors

License

This package is part of the c3p0-box/utils collection.

Documentation

Overview

Package erm provides comprehensive error management utilities for Go applications following onion/clean architecture patterns. It enriches errors with stack traces, HTTP status codes, safe user-facing messages, validation error capabilities, and error collection support for handling multiple related errors, serving as a unified error management system for both application and validation errors.

The package follows KISS and SOLID principles, uses custom i18n package for internationalization, and provides a clean API for error propagation across application layers. It unifies general application errors and validation errors under a single, consistent interface.

The package uses an interface-based design where Error is the main interface implemented by StackError. This provides flexibility while maintaining type safety and compatibility with Go's standard error handling.

Field Localization

The package supports localizing field names in error messages using i18n message keys. This allows applications to display user-friendly field names that are properly localized for different languages:

// Standard field name (not localized)
err := erm.RequiredError("email", "")
fmt.Println(err.Error()) // "email is required"

// Localized field name (variadic field message key parameter)
err := erm.RequiredError("email", "", "fields.email")
// If "fields.email" translates to "Email Address", displays: "Email Address is required"

// Manual field localization on existing errors
err := erm.NewValidationError("validation.required", "user_email", "").
	WithFieldMessageKey("fields.user_email")

Field message keys are resolved using the same i18n system as validation messages, falling back to the original field name if no translation is available.

Basic Usage

// Create errors with automatic operation detection
err := erm.New(http.StatusBadRequest, "Invalid email", originalErr)

// Use convenience constructors
err := erm.BadRequest("Invalid input", originalErr)

// Extract information safely from any error
status := erm.Status(err)   // Works with any error type
message := erm.Message(err) // Safe for user consumption

Validation Error Usage

	// Create validation errors with message keys
	err := erm.NewValidationError("validation.required", "email", "")
	err = err.WithParam("min", 5)

	// Use convenience constructors for common validations
	err := erm.RequiredError("email", "")
	err := erm.MinLengthError("password", "123", 8)

 Localized formatting with custom i18n package
	fmt.Println(err.Error()) // "email is required" or localized message

Error Collection Usage

// Create error containers and collect multiple errors
container := erm.New(http.StatusBadRequest, "Validation errors", nil)
result := container.AddError(err1).AddError(err2)
errorMap := result.ErrMap() // Localized error map

Internationalization

The package uses the custom github.com/c3p0-box/utils/i18n package for internationalization. Messages are resolved on-demand when Error() or ToError() methods are called, using the internally managed localizers.

// Get localized error messages for different languages
englishMsg := err.LocalizedError(language.English)
spanishMsg := err.LocalizedError(language.Spanish)
localizedMap := err.LocalizedErrMap(language.Spanish)

All functions handle nil errors gracefully and are safe for concurrent use. The package serves as a unified error management system that eliminates the need for separate validation error types while maintaining full compatibility with Go's standard error handling patterns.

Package erm provides localization and internationalization utilities using the custom github.com/c3p0-box/utils/i18n package.

This file contains the localization infrastructure for the ERM error management system, providing on-demand message resolution and per-language translation management.

Message Constants

This package exports validation message key constants that are used throughout the validation system. These constants provide type safety and avoid hardcoded strings in validation logic.

The constants are organized into three categories:

  • Standard validation messages (e.g., MsgRequired, MsgEmail)
  • Negated validation messages (e.g., MsgNotRequired, MsgNotEmail)
  • Special validation messages (e.g., MsgMustBeZero, MsgDivisorZero)

All constants map to actual translation keys defined in initializeMessages() and can be extended to support additional languages by calling i18n.AddTranslations() with the appropriate translations.

Index

Constants

View Source
const (
	MsgRequired    = "validation.required"
	MsgEmpty       = "validation.empty"
	MsgMinLength   = "validation.min_length"
	MsgMaxLength   = "validation.max_length"
	MsgExactLength = "validation.exact_length"

	MsgEmail        = "validation.email"
	MsgURL          = "validation.url"
	MsgNumeric      = "validation.numeric"
	MsgAlpha        = "validation.alpha"
	MsgAlphaNumeric = "validation.alpha_numeric"
	MsgRegex        = "validation.regex"
	MsgIn           = "validation.in"
	MsgNotIn        = "validation.not_in"
	MsgContains     = "validation.contains"
	MsgStartsWith   = "validation.starts_with"
	MsgEndsWith     = "validation.ends_with"
	MsgLowercase    = "validation.lowercase"
	MsgUppercase    = "validation.uppercase"
	MsgInteger      = "validation.integer"
	MsgFloat        = "validation.float"
	MsgJSON         = "validation.json"
	MsgBase64       = "validation.base64"
	MsgUUID         = "validation.uuid"
	MsgSlug         = "validation.slug"
	MsgMin          = "validation.min_value"
	MsgMax          = "validation.max_value"
	MsgBetween      = "validation.between"
	MsgZero         = "validation.zero"
	MsgEqual        = "validation.equal"
	MsgEqualTo      = "validation.equal_to"
	MsgGreaterThan  = "validation.greater_than"
	MsgLessThan     = "validation.less_than"
	MsgPositive     = "validation.positive"
	MsgNegative     = "validation.negative"
	MsgEven         = "validation.even"
	MsgOdd          = "validation.odd"
	MsgMultipleOf   = "validation.multiple_of"
	MsgFinite       = "validation.finite"
	MsgPrecision    = "validation.precision"
	MsgInvalid      = "validation.invalid"
	MsgDuplicate    = "validation.duplicate"

	MsgNotEmpty        = "validation.not_empty"
	MsgNotEqualTo      = "validation.not_equal_to"
	MsgNotMinLength    = "validation.not_min_length"
	MsgNotMaxLength    = "validation.not_max_length"
	MsgNotExactLength  = "validation.not_exact_length"
	MsgNotBetween      = "validation.not_between"
	MsgNotEmail        = "validation.not_email"
	MsgNotURL          = "validation.not_url"
	MsgNotNumeric      = "validation.not_numeric"
	MsgNotAlpha        = "validation.not_alpha"
	MsgNotAlphaNumeric = "validation.not_alpha_numeric"
	MsgNotRegex        = "validation.not_regex"
	MsgNotContains     = "validation.not_contains"
	MsgNotStartsWith   = "validation.not_starts_with"
	MsgNotEndsWith     = "validation.not_ends_with"
	MsgNotLowercase    = "validation.not_lowercase"
	MsgNotUppercase    = "validation.not_uppercase"
	MsgNotInteger      = "validation.not_integer"
	MsgNotFloat        = "validation.not_float"
	MsgNotJSON         = "validation.not_json"
	MsgNotBase64       = "validation.not_base64"
	MsgNotUUID         = "validation.not_uuid"
	MsgNotSlug         = "validation.not_slug"
	MsgNotZero         = "validation.not_zero"
	MsgNotMinValue     = "validation.not_min_value"
	MsgNotMaxValue     = "validation.not_max_value"
	MsgNotGreaterThan  = "validation.not_greater_than"
	MsgNotLessThan     = "validation.not_less_than"
	MsgNotPositive     = "validation.not_positive"
	MsgNotNegative     = "validation.not_negative"
	MsgNotEven         = "validation.not_even"
	MsgNotOdd          = "validation.not_odd"
	MsgNotMultipleOf   = "validation.not_multiple_of"
	MsgNotFinite       = "validation.not_finite"
	MsgNotPrecision    = "validation.not_precision"

	MsgMustBeZero  = "validation.must_be_zero"
	MsgDivisorZero = "validation.divisor_zero"

	MsgErrorMultiple       = "error.multiple"
	MsgErrorNotFound       = "error.not_found"
	MsgErrorInvalidRequest = "error.invalid_request"
	MsgErrorInactive       = "error.inactive"
)

Validation message key constants for localized error messages. These constants map to translation keys defined in initializeMessages(). They provide type safety and avoid hardcoded strings throughout the validation system.

View Source
const NonFieldErrors = "non_field_errors"

Variables

This section is empty.

Functions

func FormatStack

func FormatStack(err Error) string

FormatStack formats a stack trace into a human-readable string suitable for logging and debugging. Each frame shows the function name, file path, and line number.

Returns empty string if the error is nil or has no stack trace.

Example output:

main.processUser
	/app/user.go:42
main.handleRequest
	/app/handler.go:28

func Message

func Message(err error) string

Message extracts a safe user-facing message from any error. The returned message is safe to send to clients without leaking internal implementation details.

Returns:

  • For erm errors with custom message: the custom message
  • For erm errors without message: HTTP status text (e.g., "Bad Request")
  • For standard errors: "Internal Server Error"
  • For nil errors: empty string

func Stack

func Stack(err error) []uintptr

Stack extracts the stack trace from any error that supports it. Use this with FormatStack to get human-readable stack traces for logging and debugging.

Returns:

  • For erm errors: captured stack trace as program counters
  • For other errors: nil
  • For nil errors: nil

func Status

func Status(err error) int

Status extracts the HTTP status code from any error, providing a safe way to get status codes from mixed error types.

Returns:

  • For erm.Error: the actual status code
  • For standard errors: http.StatusInternalServerError (500)
  • For nil errors: http.StatusOK (200)

Types

type Error

type Error interface {
	error

	// Code returns the HTTP status code associated with this error
	Code() int

	// Unwrap returns the wrapped error for errors.Is/As compatibility
	Unwrap() error

	// Stack returns the stack trace as an array of program counters.
	// Returns nil for client errors (4xx) and non-nil for server errors (500)
	Stack() []uintptr

	// MessageKey returns the i18n message key for localization
	MessageKey() string

	// FieldName returns the field name for validation errors
	FieldName() string

	// FieldMessageKey returns the i18n message key for the field name
	FieldMessageKey() string

	// Value returns the value being validated for validation errors
	Value() interface{}

	// Params returns template parameters for i18n message substitution
	Params() map[string]interface{}

	// AddError adds another error to this error's collection.
	// This is used for collecting multiple validation errors.
	AddError(Error)

	// AddErrors adds multiple errors to this error's collection.
	// This is a convenience method for adding multiple errors at once.
	AddErrors([]Error)

	// AllErrors returns all child errors. Returns empty slice if no child errors.
	AllErrors() []Error

	// HasErrors returns true if this error contains child errors.
	HasErrors() bool

	// LocalizedError returns the localized error message for the specified language
	LocalizedError(language.Tag) string

	// LocalizedErrMap returns a map of field names to localized error messages for the specified language
	LocalizedErrMap(language.Tag) map[string][]string

	// ErrMap returns a map of field names to error messages using the default localizer
	ErrMap() map[string][]string

	// WithMessageKey sets the i18n message key and returns a new Error
	WithMessageKey(messageKey string) Error

	// WithFieldName sets the field name being validated
	WithFieldName(fieldName string) Error

	// WithFieldMessageKey sets the field message key for localization
	WithFieldMessageKey(fieldMessageKey string) Error

	// WithValue sets the value being validated
	WithValue(value interface{}) Error

	// WithParam adds a template parameter for i18n substitution
	WithParam(key string, value interface{}) Error

	// WithRootError sets the root error
	WithRootError(root error) Error
}

Error represents an enriched application error that extends the standard Go error interface with additional metadata and capabilities.

Beyond the standard Error() method, it provides:

  • Code: HTTP status codes for API responses
  • Stack: Stack traces for debugging (captured only for 500 errors)
  • Localization: Message key-based internationalization support
  • Validation: Field-level validation error capabilities
  • Error Collection: Ability to collect multiple related errors
  • Safe Messages: User-facing messages separate from internal errors

Error values are immutable after creation and safe for concurrent access. They integrate with Go's standard error handling patterns including errors.Is, errors.As, and error wrapping/unwrapping.

All methods return appropriate zero values when called on nil receivers, making Error instances safe to use without explicit nil checks in most cases.

func BadRequest

func BadRequest(msg string, err error) Error

BadRequest creates a 400 Bad Request error.

func Conflict

func Conflict(msg string, err error) Error

Conflict creates a 409 Conflict error.

func DuplicateError

func DuplicateError(fieldName string, value interface{}, fieldMessageKey ...string) Error

DuplicateError creates a "duplicate" validation error for unique constraint violations. Optionally accepts a field message key for localization as the last parameter.

func EmailError

func EmailError(fieldName string, value interface{}, fieldMessageKey ...string) Error

EmailError creates an "email" validation error. Optionally accepts a field message key for localization as the last parameter.

func Forbidden

func Forbidden(msg string, err error) Error

Forbidden creates a 403 Forbidden error.

func Internal

func Internal(msg string, err error) Error

Internal creates a 500 Internal Server Error.

func InvalidError

func InvalidError(fieldName string, value interface{}, fieldMessageKey ...string) Error

InvalidError creates an "invalid" validation error for general invalid values. Optionally accepts a field message key for localization as the last parameter.

func MaxLengthError

func MaxLengthError(fieldName string, value interface{}, max int, fieldMessageKey ...string) Error

MaxLengthError creates a "max_length" validation error with maximum length parameter. Optionally accepts a field message key for localization as the last parameter.

func MaxValueError

func MaxValueError(fieldName string, value interface{}, max interface{}, fieldMessageKey ...string) Error

MaxValueError creates a "max_value" validation error with maximum value parameter. Optionally accepts a field message key for localization as the last parameter.

func MinLengthError

func MinLengthError(fieldName string, value interface{}, min int, fieldMessageKey ...string) Error

MinLengthError creates a "min_length" validation error with minimum length parameter. Optionally accepts a field message key for localization as the last parameter.

func MinValueError

func MinValueError(fieldName string, value interface{}, min interface{}, fieldMessageKey ...string) Error

MinValueError creates a "min_value" validation error with minimum value parameter. Optionally accepts a field message key for localization as the last parameter.

func New

func New(code int, msg string, err error) Error

New creates a new Error with stack trace capture and HTTP status code. Stack traces are only captured for Internal Server Errors (HTTP 500) to optimize performance for client errors which don't need debugging information.

Parameters:

  • code: HTTP status code (if 0, defaults to http.StatusInternalServerError)
  • msg: User-safe message for client responses
  • err: Underlying error to wrap (can be nil; if nil, no root error is stored)

When err is nil, the returned Error will have a nil root error but will still contain the provided message and status code. The message can be accessed via the Error interface methods.

Stack traces are only captured for server errors (500) where debugging is needed.

Example:

err := erm.New(http.StatusBadRequest, "Invalid email format", validationErr)
// err.Code() returns 400
// err.Stack() returns nil (no stack trace for client errors)
// err.Unwrap() returns validationErr

errWithoutRoot := erm.New(http.StatusBadRequest, "Custom message", nil)
// errWithoutRoot.Unwrap() returns nil
// errWithoutRoot.Error() returns "Custom message"

serverErr := erm.New(http.StatusInternalServerError, "Database error", dbErr)
// serverErr.Stack() returns captured stack trace for debugging

func NewValidationError

func NewValidationError(messageKey, fieldName string, value interface{}, fieldMessageKey ...string) Error

NewValidationError creates a new validation error with the specified message key, field name, and value. This is the primary constructor for validation errors that will be used by the validation package. All validation errors are created with HTTP 400 Bad Request status. Optionally accepts a field message key for localization as the last parameter.

Example:

err := erm.NewValidationError("validation.required", "email", "")
err = err.WithParam("min", 5)

// With field localization:
err := erm.NewValidationError("validation.required", "email", "", "fields.email")

func NotFound

func NotFound(fieldName string, err error) Error

NotFound creates a 404 Not Found error.

func RequiredError

func RequiredError(fieldName string, value interface{}, fieldMessageKey ...string) Error

RequiredError creates a "required" validation error. Optionally accepts a field message key for localization as the last parameter.

func Unauthorized

func Unauthorized(msg string, err error) Error

Unauthorized creates a 401 Unauthorized error.

func Wrap

func Wrap(err error) Error

Wrap wraps an error with erm error capabilities while preserving the original error's metadata when possible.

Behavior:

  • If err is nil: returns nil
  • If err is already an erm error: returns it unchanged
  • If err is a standard error: wraps it with http.StatusInternalServerError

The operation context is updated to reflect the current call site, making it useful for adding operation context as errors bubble up through application layers.

type LocalizeConfig

type LocalizeConfig struct {
	MessageID    string
	TemplateData interface{}
}

LocalizeConfig provides the configuration for message localization. It maintains the same structure as go-i18n's LocalizeConfig.

type Localizer

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

Localizer is a compatibility wrapper that provides the same API as go-i18n's Localizer but uses our custom i18n package internally.

func GetLocalizer

func GetLocalizer(tag language.Tag) *Localizer

GetLocalizer returns a localizer for the specified language. This function maintains backward compatibility with the old API but now uses our custom i18n package internally. For unsupported languages, it will fall back to English messages. It's safe to call this method concurrently.

func (*Localizer) Localize

func (l *Localizer) Localize(config *LocalizeConfig) (string, error)

Localize translates a message using our custom i18n package. It maintains the same API as go-i18n's Localizer.Localize method.

func (*Localizer) MustLocalize

func (l *Localizer) MustLocalize(config *LocalizeConfig) string

MustLocalize translates a message and returns the result or the message ID if translation fails. It maintains the same API as go-i18n's Localizer.MustLocalize method.

type StackError

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

StackError represents an application error enriched with stack trace, HTTP status code, safe user-facing message, validation error capabilities, and support for collecting multiple related errors with i18n support.

StackError captures the following information when created:

  • code: HTTP status code (e.g., http.StatusBadRequest)
  • msg: Safe user-facing message for client responses
  • root: Wrapped underlying error maintaining error chain
  • stack: Stack trace as program counters for debugging (only for 500 errors)
  • messageKey: i18n message key for localization (e.g., "validation.required")
  • fieldName: Field name being validated
  • fieldMessageKey: i18n message key for localizing field names (e.g., "fields.email")
  • value: Value being validated
  • params: Template parameters for i18n substitution
  • errors: Child errors for batch validation scenarios (single-level only)

StackError values are immutable after creation and are safe for concurrent access. They satisfy Go's standard error wrapping expectations and work with errors.Is/As functions.

func (*StackError) AddError

func (e *StackError) AddError(err Error)

AddError adds a child error to this error's collection. If the added error already contains child errors, they are flattened to prevent deep nesting (only one level of error collection is allowed).

func (*StackError) AddErrors

func (e *StackError) AddErrors(errs []Error)

AddErrors adds multiple errors to this error's collection. This is a convenience method for adding multiple errors at once.

func (*StackError) AllErrors

func (e *StackError) AllErrors() []Error

AllErrors returns all child errors. Returns empty slice if no child errors.

func (*StackError) Code

func (e *StackError) Code() int

Code returns the HTTP status code associated with this error.

func (*StackError) ErrMap

func (e *StackError) ErrMap() map[string][]string

ErrMap returns a map of field names to error messages using the default English localizer. Returns nil if no errors exist. Convenience method for LocalizedErrMap(language.English).

func (*StackError) Error

func (e *StackError) Error() string

Error returns the error message as a string, satisfying Go's error interface. Uses the default localizer for internationalization when a message key is available. If child errors are present, it formats them as a collection.

func (*StackError) FieldMessageKey

func (e *StackError) FieldMessageKey() string

FieldMessageKey returns the i18n message key for the field name. Returns empty string for nil receivers or if no field message key was set.

func (*StackError) FieldName

func (e *StackError) FieldName() string

FieldName returns the field name being validated. Returns empty string for nil receivers or if no field name was set.

func (*StackError) HasErrors

func (e *StackError) HasErrors() bool

HasErrors returns true if this error contains child errors.

func (*StackError) LocalizedErrMap

func (e *StackError) LocalizedErrMap(tag language.Tag) map[string][]string

LocalizedErrMap returns a map of field names to localized error messages for the specified language.

func (*StackError) LocalizedError

func (e *StackError) LocalizedError(tag language.Tag) string

LocalizedError returns the error message for the specified language.

func (*StackError) MessageKey

func (e *StackError) MessageKey() string

MessageKey returns the i18n message key for localization. Returns empty string for nil receivers or if no message key was set.

func (*StackError) Params

func (e *StackError) Params() map[string]interface{}

Params returns the template parameters. Returns nil for nil receivers or if no parameters were set.

func (*StackError) Stack

func (e *StackError) Stack() []uintptr

Stack returns the captured stack trace as program counters. Returns nil for client errors (4xx) and stack trace for server errors (500).

func (*StackError) Unwrap

func (e *StackError) Unwrap() error

Unwrap returns the underlying error for Go 1.13+ error wrapping support.

func (*StackError) Value

func (e *StackError) Value() interface{}

Value returns the value being validated. Returns nil for nil receivers or if no value was set.

func (*StackError) WithFieldMessageKey

func (e *StackError) WithFieldMessageKey(fieldMessageKey string) Error

WithFieldMessageKey sets the field message key for localization.

func (*StackError) WithFieldName

func (e *StackError) WithFieldName(fieldName string) Error

WithFieldName sets the field name being validated.

func (*StackError) WithMessageKey

func (e *StackError) WithMessageKey(messageKey string) Error

WithMessageKey sets the i18n message key.

func (*StackError) WithParam

func (e *StackError) WithParam(key string, value interface{}) Error

WithParam adds a template parameter.

func (*StackError) WithRootError

func (e *StackError) WithRootError(root error) Error

func (*StackError) WithValue

func (e *StackError) WithValue(value interface{}) Error

WithValue sets the value being validated.

Jump to

Keyboard shortcuts

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