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
- func FormatStack(err Error) string
- func Message(err error) string
- func Stack(err error) []uintptr
- func Status(err error) int
- type Error
- func BadRequest(msg string, err error) Error
- func Conflict(msg string, err error) Error
- func DuplicateError(fieldName string, value interface{}, fieldMessageKey ...string) Error
- func EmailError(fieldName string, value interface{}, fieldMessageKey ...string) Error
- func Forbidden(msg string, err error) Error
- func Internal(msg string, err error) Error
- func InvalidError(fieldName string, value interface{}, fieldMessageKey ...string) Error
- func MaxLengthError(fieldName string, value interface{}, max int, fieldMessageKey ...string) Error
- func MaxValueError(fieldName string, value interface{}, max interface{}, ...) Error
- func MinLengthError(fieldName string, value interface{}, min int, fieldMessageKey ...string) Error
- func MinValueError(fieldName string, value interface{}, min interface{}, ...) Error
- func New(code int, msg string, err error) Error
- func NewValidationError(messageKey, fieldName string, value interface{}, fieldMessageKey ...string) Error
- func NotFound(fieldName string, err error) Error
- func RequiredError(fieldName string, value interface{}, fieldMessageKey ...string) Error
- func Unauthorized(msg string, err error) Error
- func Wrap(err error) Error
- type LocalizeConfig
- type Localizer
- type StackError
- func (e *StackError) AddError(err Error)
- func (e *StackError) AddErrors(errs []Error)
- func (e *StackError) AllErrors() []Error
- func (e *StackError) Code() int
- func (e *StackError) ErrMap() map[string][]string
- func (e *StackError) Error() string
- func (e *StackError) FieldMessageKey() string
- func (e *StackError) FieldName() string
- func (e *StackError) HasErrors() bool
- func (e *StackError) LocalizedErrMap(tag language.Tag) map[string][]string
- func (e *StackError) LocalizedError(tag language.Tag) string
- func (e *StackError) MessageKey() string
- func (e *StackError) Params() map[string]interface{}
- func (e *StackError) Stack() []uintptr
- func (e *StackError) Unwrap() error
- func (e *StackError) Value() interface{}
- func (e *StackError) WithFieldMessageKey(fieldMessageKey string) Error
- func (e *StackError) WithFieldName(fieldName string) Error
- func (e *StackError) WithMessageKey(messageKey string) Error
- func (e *StackError) WithParam(key string, value interface{}) Error
- func (e *StackError) WithRootError(root error) Error
- func (e *StackError) WithValue(value interface{}) Error
Constants ¶
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.
const NonFieldErrors = "non_field_errors"
Variables ¶
This section is empty.
Functions ¶
func FormatStack ¶
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 ¶
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
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 ¶
BadRequest creates a 400 Bad Request error.
func DuplicateError ¶
DuplicateError creates a "duplicate" validation error for unique constraint violations. Optionally accepts a field message key for localization as the last parameter.
func EmailError ¶
EmailError creates an "email" validation error. Optionally accepts a field message key for localization as the last parameter.
func InvalidError ¶
InvalidError creates an "invalid" validation error for general invalid values. Optionally accepts a field message key for localization as the last parameter.
func MaxLengthError ¶
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 ¶
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 ¶
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 RequiredError ¶
RequiredError creates a "required" validation error. Optionally accepts a field message key for localization as the last parameter.
func Unauthorized ¶
Unauthorized creates a 401 Unauthorized error.
func Wrap ¶
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 ¶
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.