errors

package
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Mar 1, 2026 License: MIT Imports: 3 Imported by: 4

README

gokit/errors

Unified application error handling with HTTP status codes, error codes, and retryable error support.

Overview

The errors module provides a structured approach to error handling across Go microservices. It moves away from simple string-based errors towards a machine-readable AppError type that includes semantic error codes, HTTP status mappings, and metadata.

This design follows best practices such as RFC 7807 (Problem Details for HTTP APIs) and Google AIP-193. It allows services to communicate clearly about what went wrong, whether the client should retry, and provides structured details that can be easily parsed by frontend applications or observability tools.

Installation

go get github.com/kbukum/gokit/errors

Quick Start

package main

import (
	"fmt"
	"net/http"

	"github.com/kbukum/gokit/errors"
)

func main() {
	// Create a structured error
	err := errors.NotFound("user", "abc-123")

	fmt.Println(err.Code)       // "NOT_FOUND"
	fmt.Println(err.HTTPStatus) // 404
	fmt.Println(err.Retryable)  // false

	// Check if it's an AppError and convert to response
	if appErr, ok := errors.AsAppError(err); ok {
		resp := appErr.ToResponse()
		// Send resp as JSON to the client
		fmt.Printf("Response: %+v\n", resp)
	}
}

Configuration

This module does not require external configuration. It uses a predefined set of error codes and mapping logic.

API Reference

Major Types
Field Type Description
AppError struct The core error type implementing the error interface.
ErrorCode string A machine-readable string representing the error category.
ErrorResponse struct The JSON-serializable structure for API responses.
Common Constructors
  • NotFound(resource, id string) *AppError: For missing resources.
  • InvalidInput(field, reason string) *AppError: For validation failures.
  • Unauthorized(reason string) *AppError: For authentication issues.
  • Forbidden(reason string) *AppError: For permission issues.
  • Internal(cause error) *AppError: For unexpected server-side errors.
  • ServiceUnavailable(service string) *AppError: For temporary outages (retryable).
Builder Methods
  • WithCause(err error): Chains an underlying error.
  • WithDetail(key string, value any): Adds a single piece of metadata.
  • WithDetails(map[string]any): Merges multiple pieces of metadata.

Advanced Usage

Error Wrapping

AppError supports Go 1.13+ error wrapping. You can use errors.Is and errors.As from the standard library.

cause := fmt.Errorf("connection refused")
appErr := errors.DatabaseError(cause)

// Unwrap works as expected
fmt.Println(errors.Unwrap(appErr) == cause) // true
Retry Logic

The Retryable flag is automatically set based on the ErrorCode. This can be used by middleware or clients to implement backoff and retry strategies.

if appErr, ok := errors.AsAppError(err); ok && appErr.Retryable {
    // Implement retry logic
}

Testing

To run the module tests:

cd errors
go test -race ./...

Contributing

Please refer to the root CONTRIBUTING.md for guidelines.

Documentation

Overview

Package errors provides unified error handling for Go services. It implements structured error types with error codes, HTTP status mapping, and retryable detection following RFC 7807 and Google AIP-193.

Package errors provides unified error handling for Go services. It implements structured error types with error codes, HTTP status mapping, and retryable detection following RFC 7807 and Google AIP-193.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsAppError

func IsAppError(err error) bool

IsAppError checks if an error is an AppError.

func IsRetryableCode

func IsRetryableCode(code ErrorCode) bool

IsRetryableCode returns true if the error code indicates a retryable error.

Types

type AppError

type AppError struct {
	// Code is a machine-readable error code.
	Code ErrorCode `json:"code"`
	// Message is a human-readable error message.
	Message string `json:"message"`
	// Retryable indicates if the operation can be retried.
	Retryable bool `json:"retryable"`
	// HTTPStatus is the recommended HTTP status code for this error.
	HTTPStatus int `json:"-"`
	// Details contains additional context for the error.
	Details map[string]any `json:"details,omitempty"`
	// Cause is the underlying error that caused this error.
	Cause error `json:"-"`
}

AppError is the unified application error type.

func AlreadyExists

func AlreadyExists(resource string) *AppError

AlreadyExists creates a new AppError for a resource that already exists.

func AsAppError

func AsAppError(err error) (*AppError, bool)

AsAppError converts an error to an AppError if possible.

func Conflict

func Conflict(reason string) *AppError

Conflict creates a new AppError for a conflict with the current state of the resource.

func ConnectionFailed

func ConnectionFailed(service string) *AppError

ConnectionFailed creates a new AppError for a failed connection to a service.

func DatabaseError

func DatabaseError(cause error) *AppError

DatabaseError creates a new AppError for a database error.

func ExternalServiceError

func ExternalServiceError(service string, cause error) *AppError

ExternalServiceError creates a new AppError for an error from an external service.

func Forbidden

func Forbidden(reason string) *AppError

Forbidden creates a new AppError for forbidden access.

func FormatResourceError

func FormatResourceError(resource string, id any) *AppError

FormatResourceError creates a not-found error with a formatted identifier.

func Internal

func Internal(cause error) *AppError

Internal creates a new AppError for an internal server error.

func InvalidFormat

func InvalidFormat(field, expectedFormat string) *AppError

InvalidFormat creates a new AppError for an invalid field format.

func InvalidInput

func InvalidInput(field, reason string) *AppError

InvalidInput creates a new AppError for invalid input.

func InvalidToken

func InvalidToken() *AppError

InvalidToken creates a new AppError for an invalid authentication token.

func MissingField

func MissingField(field string) *AppError

MissingField creates a new AppError for a missing required field.

func New

func New(code ErrorCode, message string, httpStatus int) *AppError

New creates a new AppError with automatic retryable detection.

func NotFound

func NotFound(resource, id string) *AppError

NotFound creates a new AppError for a resource that was not found.

func RateLimited

func RateLimited() *AppError

RateLimited creates a new AppError for too many requests.

func ServiceUnavailable

func ServiceUnavailable(service string) *AppError

ServiceUnavailable creates a new AppError for a service that is temporarily unavailable.

func Timeout

func Timeout(operation string) *AppError

Timeout creates a new AppError for a request that timed out.

func TokenExpired

func TokenExpired() *AppError

TokenExpired creates a new AppError for an expired authentication token.

func Unauthorized

func Unauthorized(reason string) *AppError

Unauthorized creates a new AppError for unauthorized access.

func Validation

func Validation(message string) *AppError

Validation creates a new AppError for validation errors.

func Wrap

func Wrap(err error) *AppError

Wrap converts a standard error to an AppError. If the error is already an AppError it is returned as-is; otherwise it is wrapped as Internal. Returns nil when err is nil.

func (*AppError) Error

func (e *AppError) Error() string

Error returns the string representation of the error.

func (*AppError) ToResponse

func (e *AppError) ToResponse() ErrorResponse

ToResponse converts an AppError to an ErrorResponse for JSON serialization.

func (*AppError) Unwrap

func (e *AppError) Unwrap() error

Unwrap returns the underlying cause of the error.

func (*AppError) WithCause

func (e *AppError) WithCause(cause error) *AppError

WithCause sets the underlying cause of the error and returns the receiver.

func (*AppError) WithDetail

func (e *AppError) WithDetail(key string, value any) *AppError

WithDetail sets a single detail key-value pair and returns the receiver.

func (*AppError) WithDetails

func (e *AppError) WithDetails(details map[string]any) *AppError

WithDetails merges the provided details into the error and returns the receiver.

type ErrorBody

type ErrorBody struct {
	// Code is a machine-readable error code.
	Code ErrorCode `json:"code"`
	// Message is a human-readable error message.
	Message string `json:"message"`
	// Retryable indicates if the operation can be retried.
	Retryable bool `json:"retryable"`
	// Details contains additional context for the error.
	Details map[string]any `json:"details,omitempty"`
}

ErrorBody contains the error details sent to clients.

type ErrorCode

type ErrorCode string

ErrorCode represents a machine-readable error code.

const (
	// ErrCodeServiceUnavailable indicates the service is temporarily unavailable.
	ErrCodeServiceUnavailable ErrorCode = "SERVICE_UNAVAILABLE"
	// ErrCodeConnectionFailed indicates a failed connection to a service.
	ErrCodeConnectionFailed ErrorCode = "CONNECTION_FAILED"
	// ErrCodeTimeout indicates the request timed out.
	ErrCodeTimeout ErrorCode = "TIMEOUT"
	// ErrCodeRateLimited indicates the client is rate limited.
	ErrCodeRateLimited ErrorCode = "RATE_LIMITED"
)

Connection/Availability errors (retryable)

const (
	// ErrCodeNotFound indicates the requested resource was not found.
	ErrCodeNotFound ErrorCode = "NOT_FOUND"
	// ErrCodeAlreadyExists indicates the resource already exists.
	ErrCodeAlreadyExists ErrorCode = "ALREADY_EXISTS"
	// ErrCodeConflict indicates a conflict with the current state of the resource.
	ErrCodeConflict ErrorCode = "CONFLICT"
)

Resource errors

const (
	// ErrCodeInvalidInput indicates the input is invalid.
	ErrCodeInvalidInput ErrorCode = "INVALID_INPUT"
	// ErrCodeMissingField indicates a required field is missing.
	ErrCodeMissingField ErrorCode = "MISSING_FIELD"
	// ErrCodeInvalidFormat indicates a field has an invalid format.
	ErrCodeInvalidFormat ErrorCode = "INVALID_FORMAT"
)

Validation errors

const (
	// ErrCodeUnauthorized indicates the request is unauthorized.
	ErrCodeUnauthorized ErrorCode = "UNAUTHORIZED"
	// ErrCodeForbidden indicates the request is forbidden.
	ErrCodeForbidden ErrorCode = "FORBIDDEN"
	// ErrCodeTokenExpired indicates the authentication token has expired.
	ErrCodeTokenExpired ErrorCode = "TOKEN_EXPIRED"
	// ErrCodeInvalidToken indicates the authentication token is invalid.
	ErrCodeInvalidToken ErrorCode = "INVALID_TOKEN"
)

Authentication/Authorization errors

const (
	// ErrCodeInternal indicates an internal server error.
	ErrCodeInternal ErrorCode = "INTERNAL_ERROR"
	// ErrCodeDatabaseError indicates a database error.
	ErrCodeDatabaseError ErrorCode = "DATABASE_ERROR"
	// ErrCodeExternalService indicates an error from an external service.
	ErrCodeExternalService ErrorCode = "EXTERNAL_SERVICE_ERROR"
)

Internal errors

type ErrorResponse

type ErrorResponse struct {
	// Error contains the error details.
	Error ErrorBody `json:"error"`
}

ErrorResponse is the JSON structure returned to clients following RFC 7807.

Jump to

Keyboard shortcuts

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