Rescode is a robust, MIT-licensed, open-source Go code generator that produces type-safe error code constants and error creator functions from user-supplied JSON or YAML configuration. The generated code follows Go best practices, works seamlessly with go:generate, and enables high-performance, compile-time validated error handling without any runtime map/slice lookups.
Traditional error handling in Go often involves:
- Runtime map lookups for error metadata
 - String-based error codes prone to typos
 - Inconsistent error response formats
 - No compile-time validation
 - Poor performance due to runtime lookups
 
Rescode solves these problems by generating type-safe, compile-time constants and factory functions that provide:
- Zero runtime lookups - All metadata is compile-time constants
 - Type safety - No magic strings or runtime errors
 - High performance - Up to 140x faster than map-based approaches
 - Consistent API - Standardized error handling across services
 - HTTP/gRPC ready - Built-in status code mapping
 
- β Type-safe error constants - Generate compile-time validated error codes
 - β Factory functions - Simple, consistent error creation API
 - β YAML/JSON input - Flexible configuration format with auto-detection
 - β HTTP/gRPC integration - Built-in status code mapping
 - β Error wrapping - Preserve original errors with full stack traces
 - β Additional data - Attach structured data to errors
 - β go:generate compatible - Seamless integration with Go toolchain
 - β High performance - No runtime maps or lookups
 - β Comprehensive tests - >95% test coverage with benchmarks
 - β CLI tool - Simple command-line interface
 
go get github.com/restayway/rescodeCreate an errors.yaml file:
- code: 1001
  key: UserNotFound
  message: User not found
  http: 404
  grpc: 5
  desc: The specified user could not be found in the database
- code: 1002
  key: InvalidEmail
  message: Invalid email address
  http: 400
  grpc: 3
  desc: The provided email address is not validgo run github.com/restayway/rescode/cmd/rescodegen --input errors.yaml --output errors_gen.go --package mainOr use go:generate:
//go:generate go run github.com/restayway/rescode/cmd/rescodegen --input errors.yaml --output errors_gen.go --package mainpackage main
import (
    "fmt"
    "log"
)
func main() {
    // Create simple error
    err := UserNotFound()
    fmt.Printf("Error: %v (HTTP: %d, gRPC: %d)\n", err, err.HttpCode, err.RpcCode)
    // Create error with wrapped error
    originalErr := fmt.Errorf("database connection failed")
    wrappedErr := UserNotFound(originalErr)
    fmt.Printf("Wrapped: %v\n", wrappedErr)
    fmt.Printf("Original: %v\n", wrappedErr.OriginalError())
    // Add additional data
    enrichedErr := InvalidEmail().SetData(map[string]string{
        "field": "email",
        "value": "invalid@",
    })
    fmt.Printf("JSON: %v\n", enrichedErr.JSON())
    // Use constants for logic
    if err.Code == UserNotFoundCode {
        log.Printf("Handling user not found error with code %d", UserNotFoundCode)
    }
}- code: 1001              # Required: Unique numeric error code (uint64)
  key: UserNotFound       # Required: Go identifier for the error
  message: User not found # Required: Human-readable error message
  http: 404              # Required: HTTP status code
  grpc: 5                # Required: gRPC status code (0-16)
  desc: Description      # Optional: Detailed description for documentation[
  {
    "code": 1001,
    "key": "UserNotFound",
    "message": "User not found",
    "http": 404,
    "grpc": 5,
    "desc": "The specified user could not be found in the database"
  }
]- code: Must be non-zero unique uint64
 - key: Must be valid Go identifier (PascalCase recommended)
 - message: Non-empty human-readable string
 - http: Valid HTTP status code (typically 400-599)
 - grpc: Valid gRPC status code (0-16)
 - desc: Optional description for documentation
 
| Code | gRPC Status | Description | 
|---|---|---|
| 0 | OK | Success | 
| 1 | CANCELLED | Operation cancelled | 
| 2 | UNKNOWN | Unknown error | 
| 3 | INVALID_ARGUMENT | Invalid argument | 
| 4 | DEADLINE_EXCEEDED | Deadline exceeded | 
| 5 | NOT_FOUND | Not found | 
| 6 | ALREADY_EXISTS | Already exists | 
| 7 | PERMISSION_DENIED | Permission denied | 
| 8 | RESOURCE_EXHAUSTED | Resource exhausted | 
| 9 | FAILED_PRECONDITION | Failed precondition | 
| 10 | ABORTED | Aborted | 
| 11 | OUT_OF_RANGE | Out of range | 
| 12 | UNIMPLEMENTED | Unimplemented | 
| 13 | INTERNAL | Internal error | 
| 14 | UNAVAILABLE | Unavailable | 
| 15 | DATA_LOSS | Data loss | 
| 16 | UNAUTHENTICATED | Unauthenticated | 
For the example above, rescode generates:
// Code generated by rescodegen. DO NOT EDIT.
package main
import (
    "github.com/restayway/rescode"
    "google.golang.org/grpc/codes"
)
// Error code constants
const (
    UserNotFoundCode uint64     = 1001
    UserNotFoundHTTP int        = 404
    UserNotFoundGRPC codes.Code = 5
    UserNotFoundMsg  string     = "User not found"
    UserNotFoundDesc string     = "The specified user could not be found in the database"
    InvalidEmailCode uint64     = 1002
    InvalidEmailHTTP int        = 400
    InvalidEmailGRPC codes.Code = 3
    InvalidEmailMsg  string     = "Invalid email address"
    InvalidEmailDesc string     = "The provided email address is not valid"
)
// UserNotFound creates a new UserNotFound error.
// The specified user could not be found in the database
func UserNotFound(err ...error) *rescode.RC {
    return rescode.New(UserNotFoundCode, UserNotFoundHTTP, UserNotFoundGRPC, UserNotFoundMsg)(err...)
}
// InvalidEmail creates a new InvalidEmail error.
// The provided email address is not valid
func InvalidEmail(err ...error) *rescode.RC {
    return rescode.New(InvalidEmailCode, InvalidEmailHTTP, InvalidEmailGRPC, InvalidEmailMsg)(err...)
}rescodegen [OPTIONS]
Options:
  --input     Path to YAML/JSON file containing error definitions (required)
  --output    Path to generated Go file (default: rescode_gen.go)
  --package   Go package name to use in generated code (default: directory name)
  --version   Show version information
  --help      Show help information
Examples:
  rescodegen --input errors.yaml --output errors_gen.go --package myservice
  go run github.com/restayway/rescode/cmd/rescodegen --input errors.json
For go:generate usage:
  //go:generate go run github.com/restayway/rescode/cmd/rescodegen --input errors.yaml --output errors_gen.go --package myservicetype RC struct {
    Code     uint64     // Unique error code
    Message  string     // Human-readable error message
    HttpCode int        // HTTP status code
    RpcCode  codes.Code // gRPC status code
    Data     any        // Optional additional data
}
type RcCreator func(...error) *RC// New creates an RcCreator function with the specified parameters
func New(code uint64, hCode int, rCode codes.Code, message string, data ...any) RcCreator
// Error implements the error interface
func (r *RC) Error() string
// SetData sets additional data for the error and returns the RC for chaining
func (r *RC) SetData(data any) *RC
// JSON returns a map representation of the error, optionally filtering by keys
func (r *RC) JSON(keys ...string) map[string]interface{}
// OriginalError returns the wrapped original error, if any
func (r *RC) OriginalError() error
// String returns a string representation of the error
func (r *RC) String() stringThis library implements multiple error handling approaches in Go. The benchmarks below compare their performance on an Apple M4 Pro (arm64):
| Approach | Description | 
|---|---|
| Generated | Compile-time optimized (current) | 
| Legacy | Runtime map lookup | 
| StaticCode | Static constants + maps | 
| VarCode | Variable declarations + maps | 
- ns/op: nanoseconds per operation (lower is better)
 - B/op: bytes allocated per operation (lower is better)
 - allocs/op: allocations per operation (lower is better)
 
| Test | Best Approach | Best ns/op | B/op | allocs/op | Notes | 
|---|---|---|---|---|---|
| PolicyNotFound | Generated | 0.2276 | 0 | 0 | Over 70x faster than next best, 0 alloc | 
| MultipleErrors | Generated | 0.4231 | 0 | 0 | Over 40x faster than next best, 0 alloc | 
| ErrorMessage | Generated | 0.2377 | 0 | 0 | Orders of magnitude faster, 0 alloc | 
| JSON | VarCode | 111.0 | 384 | 6 | All approaches within 4 ns/op of each other | 
Expand for full benchmark output
go test -bench="PolicyNotFound$|MultipleErrors$|ErrorMessage$|JSON$" -benchmem -count=5 goos: darwin goarch: arm64 cpu: Apple M4 Pro PolicyNotFound Generated 0.2276 ns/op 0 B/op 0 allocs/op Legacy 19.30 ns/op 80 B/op 1 allocs/op StaticCode 17.52 ns/op 80 B/op 1 allocs/op VarCode 18.15 ns/op 80 B/op 1 allocs/op MultipleErrors Generated 0.4231 ns/op 0 B/op 0 allocs/op Legacy 20.44 ns/op 80 B/op 1 allocs/op StaticCode 19.28 ns/op 80 B/op 1 allocs/op VarCode 19.13 ns/op 80 B/op 1 allocs/op ErrorMessage Generated 0.2377 ns/op 0 B/op 0 allocs/op Legacy 9.365 ns/op 0 B/op 0 allocs/op StaticCode 9.525 ns/op 0 B/op 0 allocs/op VarCode 9.547 ns/op 0 B/op 0 allocs/op JSON Generated 112.0 ns/op 384 B/op 6 allocs/op Legacy 111.4 ns/op 384 B/op 6 allocs/op StaticCode 112.5 ns/op 384 B/op 6 allocs/op VarCode 111.0 ns/op 384 B/op 6 allocs/op
- The Generated (compile-time) approach is orders of magnitude faster for simple error creation, with zero allocations.
 - For error messages, all static approaches (Legacy/StaticCode/VarCode) are very close in performance.
 - For JSON serialization, all approaches perform nearly identically, limited by Goβs native map/interface allocation and JSON encoding overhead.
 - In typical usage, any of these approaches is fast enough; only in high-throughput or critical-path code will the differences matter.
 
Tip: If you require both dynamic error construction and zero-allocation performance, prefer the Generated approach for hot paths. For API/JSON serialization, all approaches have similar performance due to Goβs encoding/json design.
See examples/basic/ for a complete basic example.
See examples/microservice/ for a full HTTP service example with error handling.
Key features demonstrated:
- HTTP status code mapping
 - JSON error responses
 - Error data attachment
 - Error wrapping
 - Type-safe error handling
 
Run tests with coverage:
go test -v -cover ./...Run benchmarks:
go test -bench=. -benchmemThe project maintains >95% test coverage with comprehensive tests for:
- Core error functionality
 - Code generation
 - CLI tool
 - Generated code validation
 - Performance benchmarks
 
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Clone the repository
 - Run tests: 
go test ./... - Run benchmarks: 
go test -bench=. - Generate examples: 
cd examples/basic && go generate 
- Follow Go best practices and idioms
 - Maintain test coverage >95%
 - Include benchmarks for performance-critical code
 - Update documentation for API changes
 
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by the need for type-safe, high-performance error handling in Go
 - Built with Go's excellent tooling ecosystem
 - Thanks to the Go community for feedback and suggestions
 
Made with β€οΈ for the Go community
