Why Error Handling Matters
When building APIs, errors are inevitable. Users will request items that donβt exist, provide invalid data, or lack permissions. How you handle these errors determines whether your API is frustrating or pleasant to use. Contract provides a portable error system that ensures your errors work correctly across REST, JSON-RPC, MCP, and other protocols - without you having to think about each one separately.The Problem with Simple Errors
Consider this code in a todo service:
With a simple
errors.New(), Contract canβt know which HTTP status to use, so it defaults to 500 Internal Server Error - even though βnot foundβ should clearly be 404.
The Solution: Contract Errors
Contract provides error types that know how to translate themselves to each protocol:
You write one error, and it works everywhere.
Creating Errors
Method 1: Error Constructor Functions (Recommended)
Contract provides convenient functions for common error types:Method 2: NewError with Code
For more control, useNewError with an explicit error code:
Method 3: Errorf with Formatting
UseErrorf when you need to include variables in the message:
Choosing the Right Error Code
Userβs Fault (4xx HTTP Status)
Use these when the client made a mistake or is requesting something they canβt have:Your Fault (5xx HTTP Status)
Use these when something went wrong on your end:Error Code Reference
Adding Context to Errors
Adding Details
Details help clients understand and handle errors better. They appear in the error response but donβt change the message:- Frontend developers: Can show βPlease enter a valid email addressβ next to the email field
- API consumers: Can programmatically handle specific error cases
- Debugging: Includes context about what went wrong
Wrapping Underlying Errors
Preserve the original error for debugging while showing a user-friendly message:WithCause method:
- Preserves the original error for logging and debugging
- Hides internal details from API responses (security!)
- Supports
errors.Is()anderrors.As()for checking causes
Complete Example
Hereβs a realistic service with proper error handling throughout:Best Practices
1. Use Specific Error Codes
Specific codes map to correct HTTP status codes:2. Write User-Friendly Messages
Messages should tell users what went wrong and what to do:3. Include Relevant Details
Details help clients handle errors intelligently:4. Donβt Leak Internal Details
Protect your infrastructure from exposure:5. Handle Each Error Case
Map different underlying errors to appropriate codes:Testing Errors
Test that your errors have the correct code and details:Common Questions
What happens if I return a regular Go error?
Regular errors are treated asInternal errors (HTTP 500):
Can I create custom error codes?
Contract uses standard codes that map consistently across protocols. For custom error semantics, use the details field:Should I log errors in my service?
Your service should return errors, not log them. Let middleware handle logging. This keeps your service focused and testable:Whatβs Next?
Now that you understand error handling:- Error Codes - Complete reference of all error codes
- REST Transport - How errors appear in REST responses
- JSON-RPC Transport - JSON-RPC error codes
- Testing - Testing error conditions