Skip to content

Repository files navigation

Errific

Errific Art

AI-Ready Error Handling for Go with caller metadata, clean error wrapping, structured context, error codes, retry metadata, and JSON serialization.

💡 Simple Example

package main
import (
"fmt""github.com/leefernandes/errific"
)
// Define your errorsvarErrUserNotFound errific.Err="user not found"funcmain() {
// Configure pretty JSON output for readabilityerrific.Configure(errific.OutputJSONPretty)
// Return an error with contexterr:=GetUser("user-123")
fmt.Println(err)
}
funcGetUser(userIDstring) error {
// Simulate error with contextreturnErrUserNotFound.
WithCode("USER_404").
WithContext(errific.Context{
"user_id": userID,
"source": "database",
})
}

Output:

{
"error": "user not found",
"code": "USER_404",
"caller": "main.go:27.GetUser",
"context": {
"source": "database",
"user_id": "user-123"
}
}

The error includes:

  • ✅ Automatic caller information (main.go:20.GetUser)
  • ✅ Error code (USER_404) visible in output
  • ✅ Structured context (user_id, source) visible in output
  • ✅ JSON output by default for structured logging

✨ Features

Core Features

  • 📍 Automatic Caller Information - File, line, and function automatically captured
  • 🔗 Clean Error Chaining - Native errors.Is and errors.As support
  • 🏷️ Error Codes & Categories - Machine-readable error classification
  • 📊 Structured Context - Attach metadata for debugging and analytics
  • 🔄 Retry Metadata - Built-in support for automated retry strategies
  • 🌐 HTTP Status Codes - Direct mapping to HTTP responses
  • 📦 JSON Serialization - Seamless integration with logging and APIs

MCP & RAG Integration

  • 🔗 MCP Error Format - JSON-RPC 2.0 compatible error responses for MCP servers
  • 🔍 Correlation Tracking - Correlation IDs, Request IDs, User IDs, Session IDs
  • 💡 Recovery Guidance - Help text, suggestions, and documentation links for AI self-healing
  • 🏷️ Semantic Tags - RAG-optimized tags for error categorization and search
  • 📌 Labels - Key-value labels for filtering, grouping, and alerting
  • Temporal Data - Timestamps and duration tracking

Quality

  • 🧵 Thread-Safe - Concurrent configuration and error creation
  • Lightweight - Small footprint, high performance
  • 🎯 98% Test Coverage - Comprehensive test suite with 72+ test cases, 13 benchmarks, 3 fuzz tests

🚀 Quick Start

Basic Usage

// Use Case: Basic error creation with automatic caller information// Keywords: basic-usage, caller-info, error-wrapping, typed-errorsvarErrDatabaseQuery errific.Err="database query failed"// Two API styles - both work!// Style 1: Explicit .New() (use when wrapping errors or caller info matters)err:=ErrDatabaseQuery.New(sqlErr)
// Style 2: Concise (recommended for new code)err:=ErrDatabaseQuery.WithCode("DB_001").WithHTTPStatus(500)
fmt.Println(err)
// Output: database query failed [myapp/db.go:42.QueryUsers]// SQL error details...

AI-Ready Error Handling

// Use Case: AI-ready error with retry metadata and structured context// Keywords: ai-ready, retry-logic, automated-recovery, structured-contextvarErrAPITimeout errific.Err="API request timeout"// Concise style (recommended) - no need to call .New() firsterr:=ErrAPITimeout.
WithCode("API_TIMEOUT_001").
WithCategory(errific.CategoryTimeout).
WithContext(errific.Context{
"endpoint": "/v1/users",
"duration_ms": 30000,
"retry_count": 2,
}).
WithRetryable(true).
WithRetryAfter(5*time.Second).
WithMaxRetries(3).
WithHTTPStatus(504)
// AI agent can now automate responsesiferrific.IsRetryable(err) {
time.Sleep(errific.GetRetryAfter(err))
// retry...
}
// Serialize for logging/monitoringjsonBytes, _:=json.Marshal(err)
log.Info(string(jsonBytes))

🎨 Output Formats & Verbosity

Errific supports multiple output formats and verbosity levels. By default, errors output as JSON with all metadata visible.

// Default: JSON format with full verbosity (shows all metadata)errific.Configure() // or Configure(OutputJSON, VerbosityFull)err:=ErrUserNotFound.
WithCode("USER_404").
WithContext(errific.Context{"user_id": "user-123"})
fmt.Println(err)
// Output: {"error":"user not found","code":"USER_404","caller":"main.go:20","context":{"user_id":"user-123"}}// JSON Pretty format (indented JSON for docs/debugging)errific.Configure(OutputJSONPretty)
fmt.Println(err)
// Output:// {// "error": "user not found",// "code": "USER_404",// "caller": "main.go:20",// "context": {// "user_id": "user-123"// }// }// Pretty format (multi-line, human-readable text)errific.Configure(OutputPretty)
fmt.Println(err)
// Output:// user not found [main.go:20.GetUser]// code: USER_404// context: map[user_id:user-123]// Compact format (single-line key=value)errific.Configure(OutputCompact)
fmt.Println(err)
// Output: user not found [main.go:20] code=USER_404 user_id=user-123// Minimal verbosity (only message + caller, useful for simple logging)errific.Configure(VerbosityMinimal)
fmt.Println(err)
// Output (JSON): {"error":"user not found","caller":"main.go:20"}// Standard verbosity (message + caller + code + category + context)errific.Configure(VerbosityStandard)
fmt.Println(err)
// Output (JSON): {"error":"user not found","code":"USER_404","caller":"main.go:20","context":{"user_id":"user-123"}}// Custom verbosity (show only specific fields)errific.Configure(VerbosityFull, HideContext, HideMCPData)
fmt.Println(err)
// Output (JSON): {"error":"user not found","code":"USER_404","caller":"main.go:20","http_status":404}

Available output formats:

  • OutputJSON (default) - Compact JSON for structured logging
  • OutputJSONPretty - Indented JSON for documentation and debugging
  • OutputPretty - Multi-line, human-readable text
  • OutputCompact - Single-line key=value pairs

Available verbosity levels:

  • VerbosityFull (default) - Show all non-empty fields
  • VerbosityStandard - Show code, category, context
  • VerbosityMinimal - Show only message and caller
  • VerbosityCustom - Use with Show*/Hide* flags for granular control

Granular field control:HideCode, HideCategory, HideContext, HideHTTPStatus, HideRetryMetadata, HideMCPData, HideTags, HideLabels, HideTimestamps

JSON Output

{
"error": "API request timeout",
"code": "API_TIMEOUT_001",
"category": "timeout",
"caller": "myapp/api.go:123.CallExternalService",
"context": {
"endpoint": "/v1/users",
"duration_ms": 30000,
"retry_count": 2
},
"retryable": true,
"retry_after": "5s",
"max_retries": 3,
"http_status": 504
}

MCP Server Integration

Scenario: Your AI tool fails during execution and needs to return a proper MCP error response.

// Use Case: MCP tool server with rich error metadata for LLM consumption// Keywords: mcp, json-rpc, llm-tools, ai-integration, error-recovery, claudevarErrToolExecution errific.Err="search_database tool failed"// Create rich error with MCP metadata (concise style)err:=ErrToolExecution.New(dbErr). // Still need .New() for wrapped errorsWithMCPCode(errific.MCPToolError). // JSON-RPC 2.0 error codeWithCorrelationID("trace-abc-123"). // Track across distributed callsWithRequestID("req-456"). // Individual request trackingWithHelp("Database connection pool exhausted"). // Human-readable helpWithSuggestion("Increase pool size to 50"). // Actionable recovery stepWithDocs("https://docs.ai/errors/db-pool"). // Documentation linkWithTags("database", "connection-pool", "retryable"). // RAG semantic tagsWithLabel("tool_name", "search_database"). // Filter/group by toolWithRetryable(true).
WithRetryAfter(5*time.Second)
// Or without wrapped error (even more concise):err:=ErrToolExecution.
WithMCPCode(errific.MCPToolError).
WithHelp("Database connection pool exhausted")
// ... rest of chain// Convert to MCP JSON-RPC 2.0 formatmcpErr:=errific.ToMCPError(err)
json.NewEncoder(w).Encode(mcpErr)

MCP Response:

{
"code": -32000,
"message": "search_database tool failed",
"data": {
"error": "search_database tool failed",
"code": "TOOL_001",
"correlation_id": "trace-abc-123",
"request_id": "req-456",
"help": "Database connection pool exhausted",
"suggestion": "Increase pool size to 50",
"docs": "https://docs.ai/errors/db-pool",
"tags": ["database", "connection-pool", "retryable"],
"labels": {"tool_name": "search_database"},
"retryable": true,
"retry_after": "5s"
}
}

Why This Matters:

  • 🤖 AI agents can self-heal using help/suggestion fields
  • 🔍 Correlation tracking across distributed MCP tool calls
  • 📊 RAG systems can categorize and search errors by semantic tags
  • 🎯 Monitoring systems can alert based on labels
  • 🔄 Automatic retry logic from metadata

🎯 Real-World Scenarios

Scenario 1: API Service Error Handling

Problem: Need consistent error responses across 50+ API endpoints

Solution: Use errific for automatic HTTP status mapping and JSON serialization

View Complete Example

Before (stdlib errors):

// Use Case: Traditional error handling without structure// Keywords: stdlib, errors, no-http-status, manual-mappingfuncGetUser(idstring) (*User, error) {
ifid=="" {
returnnil, errors.New("invalid id") // No status code, no structure
}
// API handler must manually map errors to HTTP status codes
}

After (errific):

// Use Case: Structured API errors with automatic HTTP status mapping// Keywords: api, rest, http-status, validation, automatic-mappingvarErrInvalidInput errific.Err="invalid input"funcGetUser(idstring) (*User, error) {
ifid=="" {
returnnil, ErrInvalidInput.New().
WithCode("VAL_USER_ID").
WithCategory(errific.CategoryValidation).
WithHTTPStatus(400).
WithContext(errific.Context{"field": "id"})
}
// ...
}
// API handler automatically gets status: GetHTTPStatus(err) → 400

Benefits:

  • ✅ Consistent error format across all endpoints
  • ✅ Automatic HTTP status code mapping
  • ✅ Structured context for debugging
  • ✅ JSON-ready for API responses

Scenario 2: Microservices with Distributed Tracing

Problem: Debugging errors across 10+ microservices is difficult

Solution: Use correlation IDs to trace errors through entire service chain

View Complete Example
// Use Case: Distributed tracing across microservices with correlation IDs// Keywords: microservices, distributed-tracing, correlation-id, service-mesh, observability// Service A (API Gateway)funcHandleRequest(w http.ResponseWriter, r*http.Request) {
correlationID:=uuid.New().String()
user, err:=userService.GetUser(ctx, userID, correlationID)
iferr!=nil {
// Correlation ID preserved through entire chainlog.Error("request failed",
"correlation_id", errific.GetCorrelationID(err),
"service_chain", "gateway → user-service → db-service")
}
}
// Service B (User Service)funcGetUser(ctx context.Context, id, correlationIDstring) (*User, error) {
user, err:=dbService.Query(ctx, id, correlationID)
iferr!=nil {
returnnil, ErrUserQuery.New(err).
WithCorrelationID(correlationID).
WithLabel("service", "user-service")
}
returnuser, nil
}
// Service C (DB Service)funcQuery(ctx context.Context, id, correlationIDstring) (*User, error) {
iferr:=db.QueryRow(query, id).Scan(&user); err!=nil {
returnnil, ErrDBQuery.New(err).
WithCorrelationID(correlationID). // Same ID!WithLabel("service", "db-service").
WithContext(errific.Context{"query": query, "user_id": id})
}
returnuser, nil
}

Benefits:

  • ✅ Trace errors across entire service chain with single ID
  • ✅ Service labels for filtering in log aggregation
  • ✅ Context preserved at each layer
  • ✅ Easy debugging in distributed systems

Scenario 3: AI Agent with Self-Healing

Problem: AI agent needs to automatically retry failed API calls

Solution: Use retry metadata for intelligent, automated retry logic

View Complete Example
// Use Case: AI agent with automated retry logic based on error metadata// Keywords: ai-agent, self-healing, retry-logic, automated-recovery, resiliencevarErrAPITimeout errific.Err="external API timeout"// Create API error with retry guidancefuncCallExternalAPI(endpointstring) (*Response, error) {
resp, err:=httpClient.Get(endpoint)
iferr!=nil {
returnnil, ErrAPITimeout.New(err).
WithRetryable(true).
WithRetryAfter(5*time.Second).
WithMaxRetries(3).
WithHelp("External API is temporarily unavailable").
WithSuggestion("Retry with exponential backoff")
}
returnresp, nil
}
// AI agent automatically retriesfuncAIAgent_CallWithRetry(endpointstring) (*Response, error) {
forattempt:=1; attempt<=3; attempt++ {
resp, err:=CallExternalAPI(endpoint)
iferr==nil {
returnresp, nil// Success!
}
// AI reads metadata and decidesif!errific.IsRetryable(err) {
break// Don't retry non-retryable errors
}
ifattempt>=errific.GetMaxRetries(err) {
break// Max retries reached
}
delay:=errific.GetRetryAfter(err)
log.Info("AI: Retrying", "attempt", attempt, "delay", delay)
time.Sleep(delay)
}
returnnil, err
}

Benefits:

  • ✅ AI makes intelligent retry decisions automatically
  • ✅ Help/suggestions guide recovery
  • ✅ Prevents retry storms with metadata
  • ✅ Exponential backoff built-in

Scenario 4: MCP Tool Server for LLMs

Problem: MCP tools need to return structured errors that LLMs can understand

Solution: Use MCP error format with recovery guidance for AI self-healing

View Complete Example
// Use Case: MCP tool server with LLM-readable error messages// Keywords: mcp, tool-server, llm-integration, json-rpc, ai-tools, claudevarErrToolExecution errific.Err="search_database tool failed"// MCP tool handlerfuncHandleSearchDatabase(paramsmap[string]interface{}) (interface{}, error) {
results, err:=database.Search(params["query"].(string))
iferr!=nil {
returnnil, ErrToolExecution.New(err).
WithMCPCode(errific.MCPToolError).
WithHelp("Database connection pool exhausted").
WithSuggestion("Retry in 10 seconds or simplify your query").
WithDocs("https://docs.example.com/tools/search_database").
WithTags("database", "connection-pool", "retryable").
WithRetryable(true).
WithRetryAfter(10*time.Second)
}
returnresults, nil
}
// Send MCP response to LLMfuncSendMCPResponse(w http.ResponseWriter, errerror) {
response:=map[string]interface{}{
"jsonrpc": "2.0",
"id": "req-123",
"error": errific.ToMCPError(err),
}
json.NewEncoder(w).Encode(response)
}

LLM receives:

{
"error": {
"code": -32000,
"message": "search_database tool failed",
"data": {
"help": "Database connection pool exhausted",
"suggestion": "Retry in 10 seconds or simplify your query",
"retryable": true,
"retry_after": "10s"
}
}
}

LLM can now:

  • ✅ Explain error to user with help text
  • ✅ Take action based on suggestion
  • ✅ Check retryable to decide if retry is safe
  • ✅ Use retry_after for intelligent backoff

Scenario 5: RAG System Error Categorization

Problem: Need to categorize 10,000+ errors for ML training and search

Solution: Use semantic tags and labels for RAG-optimized error indexing

View Complete Example
// Use Case: RAG system with error categorization for semantic search// Keywords: rag, semantic-search, vector-database, ml-training, error-categorization, embeddingsvarErrEmbedding errific.Err="embedding generation failed"// Create error with RAG metadatafuncGenerateEmbedding(textstring) ([]float64, error) {
embedding, err:=openai.CreateEmbedding(text)
iferr!=nil {
returnnil, ErrEmbedding.New(err).
WithTags("rag", "embedding", "openai", "rate-limit").
WithLabel("model", "text-embedding-ada-002").
WithLabel("provider", "openai").
WithHelp("OpenAI API rate limit exceeded").
WithContext(errific.Context{
"token_count": len(text),
"rate_limit": "60/min",
})
}
returnembedding, nil
}
// Index errors for RAG searchfuncIndexErrorForRAG(errerror) {
vectorDB.Store(ErrorDocument{
Tags: errific.GetTags(err), // ["rag", "embedding", "openai"]Labels: errific.GetLabels(err), // {"model": "...", "provider": "..."}Context: errific.GetContext(err), // {"token_count": 1234, ...}Help: errific.GetHelp(err), // For similarity matching
})
}
// Query similar errorsfuncQuerySimilarErrors(querystring) []ErrorDocument {
returnvectorDB.SearchByTags([]string{"embedding", "rate-limit"})
}

Benefits:

  • ✅ Semantic tags enable error categorization
  • ✅ Labels provide structured filtering (provider, model)
  • ✅ Context contains numerical features for ML
  • ✅ Help text indexed for similarity search
  • ✅ Time-series analysis with timestamps

🤔 Decision Guide

Which Features Do I Need?

Start: I have an error
│
├─ Need debugging info? → Use .New() (automatic caller)
├─ Building an API? → Use .WithHTTPStatus() + .WithCategory()
├─ Need retry logic? → Use .WithRetryable() + .WithRetryAfter()
├─ Distributed system? → Use .WithCorrelationID()
├─ MCP server for LLMs? → Use .WithMCPCode() + .WithHelp()
└─ RAG/ML system? → Use .WithTags() + .WithLabels()

Quick Reference Table

FeatureMethodWhen to UseExample Use Case
Automatic Caller.New()AlwaysDebug which function failed
Error Codes.WithCode()Monitoring, alerts"Alert on ERR_DB_001"
Categories.WithCategory()Routing, HTTP mapping"Return 400 for validation errors"
Context Data.WithContext()Debugging, logging"What parameters caused this?"
Retry Logic.WithRetryable()Resilience, automation"AI agent auto-retry"
HTTP Status.WithHTTPStatus()API services"Auto-map to HTTP response"
MCP Codes.WithMCPCode()MCP servers"LLM-readable errors"
Recovery Help.WithHelp()AI self-healing"Guide automated recovery"
Correlation IDs.WithCorrelationID()Distributed tracing"Trace across services"
Semantic Tags.WithTags()RAG, search, ML"Categorize for training"
Labels.WithLabels()Filtering, grouping"Alert by severity"

📖 Documentation

Error Categories

// Use Case: Error categories for routing and HTTP status code mapping// Keywords: categories, classification, http-mapping, error-routingCategoryClient// 4xx - client errorsCategoryServer// 5xx - server errorsCategoryNetwork// connectivity issuesCategoryValidation// input validationCategoryNotFound// 404 errorsCategoryUnauthorized// 401/403 errorsCategoryTimeout// timeout errors

Key Methods

// Use Case: Common error enrichment patterns and metadata extraction// Keywords: methods, api-reference, error-enrichment, metadata-extraction// Structured context
.WithContext(Context{"key": "value"})
// Machine-readable codes
.WithCode("ERR_001")
.WithCategory(CategoryServer)
// Retry automation
.WithRetryable(true)
.WithRetryAfter(5*time.Second)
.WithMaxRetries(3)
// HTTP integration
.WithHTTPStatus(503)
// Extract metadataGetCode(err) // → "ERR_001"GetCategory(err) // → CategoryServerIsRetryable(err) // → trueGetHTTPStatus(err) // → 503GetContext(err) // → Context map

🎯 Use Cases

  • API Services - Automatic HTTP status code mapping and JSON responses
  • Microservices - Structured logging with correlation IDs and context
  • Retry Logic - Built-in retry metadata for resilience patterns
  • AI Agents - Machine-readable error codes and categories for automation
  • Monitoring - JSON serialization for Datadog, ELK, Prometheus
  • Debugging - Automatic caller information and stack traces

📊 More Examples

Check out the comprehensive examples including:

  • Context attachment
  • Error codes and categories
  • Retry metadata
  • JSON serialization
  • AI agent scenarios
  • HTTP integration

Try it on the playground!

📚 RAG-Optimized Documentation

For AI agents and RAG systems, comprehensive documentation is available:

  • API Reference - Complete API documentation with examples, decision trees, and troubleshooting
  • Decision Guide - When to use each feature, error handling patterns, and automation guides
  • Docs Index - Documentation overview with semantic tags and FAQ

Each document is self-contained with full context for RAG retrieval.

📊 Coverage & Quality

  • 98.1% test coverage with 72+ test cases
  • 13 benchmarks for performance validation
  • 3 fuzz tests for robustness (315K+ executions, 0 crashes)
  • 5 integration tests for real-world scenarios
  • Thread-safe (race detector clean)
  • Zero external dependencies
  • Comprehensive examples and documentation

About

AI-Ready Error Handling for Go with caller metadata, clean error wrapping, structured context, error codes, retry metadata, and JSON serialization.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages