mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
237 lines
6.4 KiB
Go
237 lines
6.4 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// Package errors provides common error types and utilities for the Lux node.
|
|
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// Common sentinel errors used throughout the codebase
|
|
var (
|
|
// Database errors
|
|
ErrNotFound = errors.New("not found")
|
|
ErrClosed = errors.New("closed")
|
|
ErrCorrupted = errors.New("corrupted")
|
|
ErrReadOnly = errors.New("read only")
|
|
ErrDiskFull = errors.New("disk full")
|
|
ErrInvalidKey = errors.New("invalid key")
|
|
ErrInvalidValue = errors.New("invalid value")
|
|
|
|
// Network errors
|
|
ErrTimeout = errors.New("timeout")
|
|
ErrConnectionClosed = errors.New("connection closed")
|
|
ErrConnectionReset = errors.New("connection reset")
|
|
ErrNoRoute = errors.New("no route to host")
|
|
ErrRefused = errors.New("connection refused")
|
|
ErrNetworkDown = errors.New("network down")
|
|
|
|
// Validation errors
|
|
ErrInvalidInput = errors.New("invalid input")
|
|
ErrInvalidSignature = errors.New("invalid signature")
|
|
ErrInvalidChecksum = errors.New("invalid checksum")
|
|
ErrInvalidFormat = errors.New("invalid format")
|
|
ErrMissingField = errors.New("missing required field")
|
|
ErrFieldTooLarge = errors.New("field exceeds maximum size")
|
|
|
|
// State errors
|
|
ErrNotInitialized = errors.New("not initialized")
|
|
ErrAlreadyExists = errors.New("already exists")
|
|
ErrNotSupported = errors.New("not supported")
|
|
ErrDeprecated = errors.New("deprecated")
|
|
ErrConflict = errors.New("conflict")
|
|
ErrCanceled = errors.New("canceled")
|
|
|
|
// Resource errors
|
|
ErrResourceExhausted = errors.New("resource exhausted")
|
|
ErrQuotaExceeded = errors.New("quota exceeded")
|
|
ErrRateLimited = errors.New("rate limited")
|
|
ErrOutOfMemory = errors.New("out of memory")
|
|
|
|
// Permission errors
|
|
ErrUnauthorized = errors.New("unauthorized")
|
|
ErrForbidden = errors.New("forbidden")
|
|
ErrAccessDenied = errors.New("access denied")
|
|
)
|
|
|
|
// Error categories for grouping related errors
|
|
type Category string
|
|
|
|
const (
|
|
CategoryDatabase Category = "database"
|
|
CategoryNetwork Category = "network"
|
|
CategoryValidation Category = "validation"
|
|
CategoryState Category = "state"
|
|
CategoryResource Category = "resource"
|
|
CategoryPermission Category = "permission"
|
|
CategoryInternal Category = "internal"
|
|
CategoryUnknown Category = "unknown"
|
|
)
|
|
|
|
// WrappedError provides context around an error
|
|
type WrappedError struct {
|
|
Err error
|
|
Category Category
|
|
Message string
|
|
Context map[string]interface{}
|
|
}
|
|
|
|
// Error implements the error interface
|
|
func (e *WrappedError) Error() string {
|
|
if e.Message != "" {
|
|
return fmt.Sprintf("[%s] %s: %v", e.Category, e.Message, e.Err)
|
|
}
|
|
return fmt.Sprintf("[%s] %v", e.Category, e.Err)
|
|
}
|
|
|
|
// Unwrap returns the underlying error
|
|
func (e *WrappedError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Is checks if the error matches a target error
|
|
func (e *WrappedError) Is(target error) bool {
|
|
return errors.Is(e.Err, target)
|
|
}
|
|
|
|
// Wrap creates a new WrappedError with the given category and message
|
|
func Wrap(err error, category Category, message string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return &WrappedError{
|
|
Err: err,
|
|
Category: category,
|
|
Message: message,
|
|
Context: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// WrapWithContext creates a new WrappedError with context information
|
|
func WrapWithContext(err error, category Category, message string, context map[string]interface{}) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return &WrappedError{
|
|
Err: err,
|
|
Category: category,
|
|
Message: message,
|
|
Context: context,
|
|
}
|
|
}
|
|
|
|
// IsNotFound checks if an error is a "not found" error
|
|
func IsNotFound(err error) bool {
|
|
return errors.Is(err, ErrNotFound)
|
|
}
|
|
|
|
// IsClosed checks if an error indicates a closed resource
|
|
func IsClosed(err error) bool {
|
|
return errors.Is(err, ErrClosed)
|
|
}
|
|
|
|
// IsTimeout checks if an error is a timeout
|
|
func IsTimeout(err error) bool {
|
|
return errors.Is(err, ErrTimeout)
|
|
}
|
|
|
|
// IsTemporary checks if an error is temporary and can be retried
|
|
func IsTemporary(err error) bool {
|
|
// Check for common temporary errors
|
|
if errors.Is(err, ErrTimeout) ||
|
|
errors.Is(err, ErrRateLimited) ||
|
|
errors.Is(err, ErrResourceExhausted) {
|
|
return true
|
|
}
|
|
|
|
// Check if error implements Temporary() method
|
|
type temporary interface {
|
|
Temporary() bool
|
|
}
|
|
if temp, ok := err.(temporary); ok {
|
|
return temp.Temporary()
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsPermanent checks if an error is permanent and should not be retried
|
|
func IsPermanent(err error) bool {
|
|
// Check for common permanent errors
|
|
return errors.Is(err, ErrNotSupported) ||
|
|
errors.Is(err, ErrDeprecated) ||
|
|
errors.Is(err, ErrInvalidInput) ||
|
|
errors.Is(err, ErrInvalidSignature) ||
|
|
errors.Is(err, ErrInvalidFormat) ||
|
|
errors.Is(err, ErrForbidden) ||
|
|
errors.Is(err, ErrUnauthorized)
|
|
}
|
|
|
|
// GetCategory returns the category of an error
|
|
func GetCategory(err error) Category {
|
|
var wrapped *WrappedError
|
|
if errors.As(err, &wrapped) {
|
|
return wrapped.Category
|
|
}
|
|
|
|
// Try to infer category from error type
|
|
switch {
|
|
case errors.Is(err, ErrNotFound) || errors.Is(err, ErrClosed):
|
|
return CategoryDatabase
|
|
case errors.Is(err, ErrTimeout) || errors.Is(err, ErrConnectionClosed):
|
|
return CategoryNetwork
|
|
case errors.Is(err, ErrInvalidInput) || errors.Is(err, ErrInvalidSignature):
|
|
return CategoryValidation
|
|
case errors.Is(err, ErrNotInitialized) || errors.Is(err, ErrAlreadyExists):
|
|
return CategoryState
|
|
case errors.Is(err, ErrResourceExhausted) || errors.Is(err, ErrOutOfMemory):
|
|
return CategoryResource
|
|
case errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden):
|
|
return CategoryPermission
|
|
default:
|
|
return CategoryUnknown
|
|
}
|
|
}
|
|
|
|
// Multi combines multiple errors into a single error
|
|
type Multi struct {
|
|
Errors []error
|
|
}
|
|
|
|
// Error implements the error interface
|
|
func (m *Multi) Error() string {
|
|
if len(m.Errors) == 0 {
|
|
return "no errors"
|
|
}
|
|
if len(m.Errors) == 1 {
|
|
return m.Errors[0].Error()
|
|
}
|
|
return fmt.Sprintf("multiple errors: %v", m.Errors)
|
|
}
|
|
|
|
// Add adds an error to the multi-error
|
|
func (m *Multi) Add(err error) {
|
|
if err != nil {
|
|
m.Errors = append(m.Errors, err)
|
|
}
|
|
}
|
|
|
|
// Err returns nil if there are no errors, otherwise returns the Multi error
|
|
func (m *Multi) Err() error {
|
|
if len(m.Errors) == 0 {
|
|
return nil
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Join combines multiple errors into a single error
|
|
func Join(errs ...error) error {
|
|
multi := &Multi{}
|
|
for _, err := range errs {
|
|
multi.Add(err)
|
|
}
|
|
return multi.Err()
|
|
}
|