AI-Ready Error Handling for Go with caller metadata, clean error wrapping, structured context, error codes, retry metadata, and JSON serialization.
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
- 📍 Automatic Caller Information - File, line, and function automatically captured
- 🔗 Clean Error Chaining - Native
errors.Isanderrors.Assupport - 🏷️ 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
- 🧵 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
// 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...// 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))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 loggingOutputJSONPretty- Indented JSON for documentation and debuggingOutputPretty- Multi-line, human-readable textOutputCompact- Single-line key=value pairs
Available verbosity levels:
VerbosityFull(default) - Show all non-empty fieldsVerbosityStandard- Show code, category, contextVerbosityMinimal- Show only message and callerVerbosityCustom- Use withShow*/Hide*flags for granular control
Granular field control:HideCode, HideCategory, HideContext, HideHTTPStatus, HideRetryMetadata, HideMCPData, HideTags, HideLabels, HideTimestamps
{
"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
}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
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) → 400Benefits:
- ✅ Consistent error format across all endpoints
- ✅ Automatic HTTP status code mapping
- ✅ Structured context for debugging
- ✅ JSON-ready for API responses
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
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
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
helptext - ✅ Take action based on
suggestion - ✅ Check
retryableto decide if retry is safe - ✅ Use
retry_afterfor intelligent backoff
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
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()
| Feature | Method | When to Use | Example Use Case |
|---|---|---|---|
| Automatic Caller | .New() | Always | Debug 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" |
// 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// 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- 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
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!
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.
- 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
