Skip to content

Enhancement: Implement error categorization system #33

Open
@wroersma

Description

@wroersma

Description

The application currently lacks a structured error categorization system. Implementing error categories would improve error handling, logging, monitoring, and user experience by providing clearer context about failure types.

Current Behavior

  • Errors are wrapped with descriptive messages
  • No systematic categorization of error types
  • Difficult to handle different error types programmatically
  • No clear separation between user errors vs system errors

Expected Behavior

  • Implement error categories (Auth, Input, GitHub API, Internal, etc.)
  • Provide structured error types with categories
  • Enable programmatic error handling based on category
  • Improve error messages and logging

Suggested Implementation

1. Define error categories:

// pkg/common/error_types.go
package common

type ErrorCategory int

const (
    ErrorCategoryUnknown ErrorCategory = iota
    ErrorCategoryAuth           // Authentication/authorization errors
    ErrorCategoryInput          // Invalid input/validation errors  
    ErrorCategoryGitHub         // GitHub API errors
    ErrorCategoryNetwork        // Network connectivity errors
    ErrorCategoryRateLimit      // Rate limiting errors
    ErrorCategoryFilesystem     // File system operation errors
    ErrorCategoryInternal       // Internal application errors
    ErrorCategoryConfiguration  // Configuration errors
)

func (c ErrorCategory) String() string {
    categories := []string{
        "Unknown",
        "Authentication",
        "Input Validation",
        "GitHub API",
        "Network",
        "Rate Limit",
        "Filesystem",
        "Internal",
        "Configuration",
    }
    if int(c) < len(categories) {
        return categories[c]
    }
    return "Unknown"
}

2. Create categorized error type:

// CategorizedError wraps an error with a category
type CategorizedError struct {
    Category ErrorCategory
    Message  string
    Err      error
}

func (e *CategorizedError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("[%s] %s: %v", e.Category, e.Message, e.Err)
    }
    return fmt.Sprintf("[%s] %s", e.Category, e.Message)
}

func (e *CategorizedError) Unwrap() error {
    return e.Err
}

// Helper functions
func NewAuthError(message string, err error) error {
    return &CategorizedError{
        Category: ErrorCategoryAuth,
        Message:  message,
        Err:      err,
    }
}

func NewInputError(message string, err error) error {
    return &CategorizedError{
        Category: ErrorCategoryInput,
        Message:  message,
        Err:      err,
    }
}

// ... similar helpers for other categories

3. Update error handling throughout codebase:

// Example in githubutils.go
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
    return NewAuthError("GitHub authentication failed", err)
}

if resp != nil && resp.StatusCode == http.StatusForbidden && resp.Rate.Remaining == 0 {
    return NewRateLimitError("GitHub API rate limit exceeded", err)
}

// Example in scanner.go
if version == "" {
    return nil, NewInputError("invalid action reference format", 
        fmt.Errorf("missing version in: %s", ref))
}

4. Add error category checking utilities:

// GetErrorCategory extracts the category from an error
func GetErrorCategory(err error) ErrorCategory {
    var catErr *CategorizedError
    if errors.As(err, &catErr) {
        return catErr.Category
    }
    return ErrorCategoryUnknown
}

// IsErrorCategory checks if an error belongs to a specific category
func IsErrorCategory(err error, category ErrorCategory) bool {
    return GetErrorCategory(err) == category
}

5. Use in main application:

// In main.go
if err != nil {
    category := common.GetErrorCategory(err)
    switch category {
    case common.ErrorCategoryAuth:
        log.Fatalf("Authentication error: %v\nPlease check your GitHub token", err)
    case common.ErrorCategoryInput:
        log.Fatalf("Invalid input: %v", err)
    case common.ErrorCategoryRateLimit:
        log.Fatalf("Rate limit exceeded: %v\nPlease try again later", err)
    default:
        log.Fatalf("Error: %v", err)
    }
}

Benefits

  • Structured error handling
  • Better error messages for users
  • Easier debugging and monitoring
  • Programmatic error handling
  • Consistent error reporting

Migration Strategy

  1. Add error categorization infrastructure
  2. Gradually update error returns throughout codebase
  3. Update error handling in main and tests
  4. Document error categories in API documentation

Test Cases

  • Verify error categorization works correctly
  • Test error unwrapping maintains category
  • Ensure backward compatibility
  • Test category-based error handling

References

  • Similar to Go 1.13+ error wrapping patterns
  • Inspired by gRPC status codes
  • Current error constants: /pkg/common/error_util.go

Labels

  • enhancement
  • error-handling
  • developer-experience

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions