The Errors API provides a structured error system with HTTP status codes and gRPC status integration.
Types
Error
Status error with code, reason, message, and metadata.
type Error struct {
Status
// contains filtered or unexported fields
}
type Status struct {
Code int32 `json:"code"`
Reason string `json:"reason"`
Message string `json:"message"`
Metadata map[string]string `json:"metadata"`
}
Human-readable error message
Additional error metadata
Constants
const (
UnknownCode = 500
UnknownReason = ""
)
Functions
New
Creates a new error.
func New(code int, reason, message string) *Error
Example:
err := errors.New(404, "USER_NOT_FOUND", "User not found")
Newf
Creates a new error with formatted message.
func Newf(code int, reason, format string, a ...any) *Error
Example:
err := errors.Newf(404, "USER_NOT_FOUND", "User %s not found", userID)
Errorf
Creates an error with formatted message.
func Errorf(code int, reason, format string, a ...any) error
FromError
Converts any error to *Error.
func FromError(err error) *Error
Error to convert. Supports wrapped errors.
Example:
if err != nil {
e := errors.FromError(err)
fmt.Printf("Code: %d, Reason: %s\n", e.Code, e.Reason)
}
Code
Returns the HTTP code for an error.
Error to extract code from
HTTP status code (200 if err is nil)
Reason
Returns the reason for an error.
func Reason(err error) string
Error to extract reason from
Clone
Deep clones an error.
func Clone(err *Error) *Error
Error Methods
Error
Returns the error string.
func (e *Error) Error() string
Unwrap
Provides compatibility for Go 1.13 error chains.
func (e *Error) Unwrap() error
Matches each error in the chain.
func (e *Error) Is(err error) bool
WithCause
Adds the underlying cause.
func (e *Error) WithCause(cause error) *Error
Example:
dbErr := sql.ErrNoRows
err := errors.NotFound("USER_NOT_FOUND", "User not found").WithCause(dbErr)
Adds metadata to the error.
func (e *Error) WithMetadata(md map[string]string) *Error
Example:
err := errors.BadRequest("INVALID_INPUT", "Invalid input").WithMetadata(map[string]string{
"field": "email",
"rule": "format",
})
GRPCStatus
Returns the gRPC status.
func (e *Error) GRPCStatus() *status.Status
HTTP Status Code Constructors
BadRequest (400)
func BadRequest(reason, message string) *Error
func IsBadRequest(err error) bool
Unauthorized (401)
func Unauthorized(reason, message string) *Error
func IsUnauthorized(err error) bool
Forbidden (403)
func Forbidden(reason, message string) *Error
func IsForbidden(err error) bool
NotFound (404)
func NotFound(reason, message string) *Error
func IsNotFound(err error) bool
Conflict (409)
func Conflict(reason, message string) *Error
func IsConflict(err error) bool
TooManyRequests (429)
func TooManyRequests(reason, message string) *Error
func IsTooManyRequests(err error) bool
ClientClosed (499)
func ClientClosed(reason, message string) *Error
func IsClientClosed(err error) bool
InternalServer (500)
func InternalServer(reason, message string) *Error
func IsInternalServer(err error) bool
ServiceUnavailable (503)
func ServiceUnavailable(reason, message string) *Error
func IsServiceUnavailable(err error) bool
GatewayTimeout (504)
func GatewayTimeout(reason, message string) *Error
func IsGatewayTimeout(err error) bool
Usage Examples
Creating Errors
package main
import (
"github.com/go-kratos/kratos/v2/errors"
)
func GetUser(id string) (*User, error) {
user, err := db.FindUser(id)
if err != nil {
return nil, errors.NotFound("USER_NOT_FOUND", "User not found").WithCause(err)
}
return user, nil
}
func CreateUser(req *CreateUserRequest) error {
if req.Email == "" {
return errors.BadRequest(
"INVALID_EMAIL",
"Email is required",
).WithMetadata(map[string]string{
"field": "email",
})
}
return nil
}
Error Checking
func handleError(err error) {
if errors.IsNotFound(err) {
// Handle not found
fmt.Println("Resource not found")
return
}
if errors.IsUnauthorized(err) {
// Handle unauthorized
fmt.Println("Unauthorized access")
return
}
// Check error code
code := errors.Code(err)
reason := errors.Reason(err)
fmt.Printf("Error: code=%d, reason=%s\n", code, reason)
}
Error Conversion
func convertError(err error) *errors.Error {
// Convert any error to Kratos error
e := errors.FromError(err)
// Access error details
fmt.Printf("Code: %d\n", e.Code)
fmt.Printf("Reason: %s\n", e.Reason)
fmt.Printf("Message: %s\n", e.Message)
for k, v := range e.Metadata {
fmt.Printf("Metadata %s: %s\n", k, v)
}
return e
}
HTTP Handler Integration
func (s *Server) GetUser(ctx context.Context, req *GetUserRequest) (*User, error) {
user, err := s.userService.Get(ctx, req.Id)
if err != nil {
// Return appropriate error
if err == sql.ErrNoRows {
return nil, errors.NotFound(
"USER_NOT_FOUND",
"User not found",
).WithCause(err)
}
return nil, errors.InternalServer(
"DATABASE_ERROR",
"Failed to query user",
).WithCause(err)
}
return user, nil
}
Error Chaining
func processOrder(orderID string) error {
// Original error from database
err := db.LockOrder(orderID)
if err != nil {
// Wrap with application error
return errors.Conflict(
"ORDER_LOCKED",
"Order is being processed",
).WithCause(err)
}
return nil
}
// Later in the code
err := processOrder("123")
if err != nil {
// Can check both the application error and the cause
if errors.IsConflict(err) {
e := errors.FromError(err)
// Access the original error via Unwrap
originalErr := e.Unwrap()
}
}