Documentation Index Fetch the complete documentation index at: https://mintlify.com/go-kratos/kratos/llms.txt
Use this file to discover all available pages before exploring further.
Kratos provides a rich error system that works seamlessly across HTTP and gRPC transports with support for error codes, reasons, metadata, and cause chains.
Error Structure
type Error struct {
Status
cause error
}
The Error type includes:
Code : HTTP/gRPC status code
Reason : Application-specific error reason
Message : Human-readable error message
Metadata : Additional error context
Cause : Underlying error (for error chains)
Creating Errors
Basic Error
import " github.com/go-kratos/kratos/v2/errors "
err := errors . New ( 404 , "USER_NOT_FOUND" , "user not found" )
// Code: 404
// Reason: USER_NOT_FOUND
// Message: user not found
err := errors . Newf ( 500 , "DATABASE_ERROR" , "failed to query user: %s " , username )
err := errors . New ( 400 , "VALIDATION_ERROR" , "invalid input" )
err = err . WithMetadata ( map [ string ] string {
"field" : "email" ,
"value" : "invalid-email" ,
})
With Cause
originalErr := database . Query ()
err := errors . New ( 500 , "DATABASE_ERROR" , "query failed" )
err = err . WithCause ( originalErr )
// Access underlying error
cause := errors . Unwrap ( err ) // Returns originalErr
Predefined Errors
Kratos provides constructors for common HTTP status codes:
// 400 Bad Request
errors . BadRequest ( "INVALID_PARAM" , "invalid parameter" )
// 401 Unauthorized
errors . Unauthorized ( "TOKEN_EXPIRED" , "authentication token expired" )
// 403 Forbidden
errors . Forbidden ( "ACCESS_DENIED" , "access denied" )
// 404 Not Found
errors . NotFound ( "RESOURCE_NOT_FOUND" , "resource not found" )
// 409 Conflict
errors . Conflict ( "ALREADY_EXISTS" , "resource already exists" )
// 500 Internal Server Error
errors . InternalServer ( "INTERNAL_ERROR" , "internal server error" )
// 503 Service Unavailable
errors . ServiceUnavailable ( "SERVICE_DOWN" , "service temporarily unavailable" )
// 504 Gateway Timeout
errors . GatewayTimeout ( "TIMEOUT" , "request timeout" )
Error Inspection
Get Error Code
err := someOperation ()
code := errors . Code ( err )
if code == 404 {
// Handle not found
}
Get Error Reason
err := someOperation ()
reason := errors . Reason ( err )
if reason == "USER_NOT_FOUND" {
// Handle specific error
}
Convert to Kratos Error
err := someOperation ()
if err != nil {
kratosErr := errors . FromError ( err )
log . Errorf ( "Code: %d , Reason: %s , Message: %s " ,
kratosErr . Code , kratosErr . Reason , kratosErr . Message )
}
Error Matching
targetErr := errors . NotFound ( "USER_NOT_FOUND" , "" )
err := someOperation ()
if errors . Is ( err , targetErr ) {
// Error matches code and reason
}
HTTP Integration
Errors are automatically converted to appropriate HTTP responses:
import (
" github.com/go-kratos/kratos/v2/errors "
" github.com/go-kratos/kratos/v2/transport/http "
)
func HandleGetUser ( ctx http . Context ) error {
user , err := userRepo . Get ( ctx , userID )
if err != nil {
// Return Kratos error
return errors . NotFound ( "USER_NOT_FOUND" , "user not found" )
}
return ctx . Result ( 200 , user )
}
// HTTP Response:
// Status: 404
// Body: {
// "code": 404,
// "reason": "USER_NOT_FOUND",
// "message": "user not found"
// }
Custom Error Encoder
func customErrorEncoder ( w http . ResponseWriter , r * http . Request , err error ) {
se := errors . FromError ( err )
// Custom error response format
response := map [ string ] interface {}{
"error" : map [ string ] interface {}{
"code" : se . Code ,
"reason" : se . Reason ,
"message" : se . Message ,
"details" : se . Metadata ,
},
}
w . Header (). Set ( "Content-Type" , "application/json" )
w . WriteHeader ( int ( se . Code ))
json . NewEncoder ( w ). Encode ( response )
}
httpSrv := http . NewServer (
http . ErrorEncoder ( customErrorEncoder ),
)
gRPC Integration
Errors are automatically converted to gRPC status:
import (
" github.com/go-kratos/kratos/v2/errors "
)
func ( s * GreeterService ) SayHello ( ctx context . Context , req * pb . HelloRequest ) ( * pb . HelloReply , error ) {
if req . Name == "" {
return nil , errors . BadRequest ( "EMPTY_NAME" , "name is required" )
}
return & pb . HelloReply { Message : "Hello " + req . Name }, nil
}
// gRPC Client receives:
// Code: InvalidArgument
// Message: name is required
// Details: ErrorInfo with Reason=EMPTY_NAME
GRPCStatus
Convert to native gRPC status:
err := errors . New ( 404 , "NOT_FOUND" , "resource not found" )
grpcStatus := err . GRPCStatus ()
// Returns *status.Status with ErrorInfo details
Error Chains
Wrapping Errors
import " github.com/go-kratos/kratos/v2/errors "
func GetUser ( id int ) ( * User , error ) {
user , err := db . Query ( id )
if err != nil {
return nil , errors . InternalServer ( "DATABASE_ERROR" , "failed to query user" ). WithCause ( err )
}
return user , nil
}
func HandleRequest ( ctx context . Context ) error {
user , err := GetUser ( 123 )
if err != nil {
// Original database error is preserved in cause
return errors . InternalServer ( "REQUEST_FAILED" , "request processing failed" ). WithCause ( err )
}
return nil
}
Unwrapping Errors
err := someOperation ()
// Get underlying error
if se := errors . FromError ( err ); se != nil {
cause := errors . Unwrap ( se )
log . Errorf ( "Caused by: %v " , cause )
}
Attach additional context to errors:
err := errors . BadRequest ( "VALIDATION_ERROR" , "validation failed" )
err = err . WithMetadata ( map [ string ] string {
"field" : "email" ,
"value" : "invalid@" ,
"constraint" : "must be valid email" ,
})
// Access metadata
se := errors . FromError ( err )
for key , value := range se . Metadata {
log . Infof ( " %s : %s " , key , value )
}
Best Practices
Use UPPER_SNAKE_CASE reasons that clearly identify the error type (e.g., USER_NOT_FOUND, INVALID_TOKEN).
Include Context in Messages
Provide helpful error messages that aid in debugging without exposing sensitive information.
Use WithCause() to preserve underlying errors for debugging while returning clean errors to clients.
Don't Expose Internal Errors
Convert internal errors (like database errors) to appropriate public errors before returning to clients.
Add validation errors and field-specific information to metadata rather than cramming it into the message.
Define error reasons as constants and use them consistently across your application.
Error Constants Example
package errs
import " github.com/go-kratos/kratos/v2/errors "
// Define error reasons as constants
const (
ReasonUserNotFound = "USER_NOT_FOUND"
ReasonInvalidEmail = "INVALID_EMAIL"
ReasonEmailExists = "EMAIL_EXISTS"
ReasonUnauthorized = "UNAUTHORIZED"
ReasonTokenExpired = "TOKEN_EXPIRED"
ReasonInternalError = "INTERNAL_ERROR"
)
// Error constructors
func ErrorUserNotFound ( msg string ) * errors . Error {
return errors . NotFound ( ReasonUserNotFound , msg )
}
func ErrorInvalidEmail ( email string ) * errors . Error {
return errors . BadRequest ( ReasonInvalidEmail , "invalid email" ). WithMetadata ( map [ string ] string {
"email" : email ,
})
}
func ErrorEmailExists ( email string ) * errors . Error {
return errors . Conflict ( ReasonEmailExists , "email already exists" ). WithMetadata ( map [ string ] string {
"email" : email ,
})
}
// Usage
func CreateUser ( email string ) error {
if ! isValidEmail ( email ) {
return errs . ErrorInvalidEmail ( email )
}
exists , err := checkEmailExists ( email )
if err != nil {
return errors . InternalServer ( errs . ReasonInternalError , "failed to check email" ). WithCause ( err )
}
if exists {
return errs . ErrorEmailExists ( email )
}
return nil
}
HTTP Transport HTTP error encoding
gRPC Transport gRPC error handling
Middleware Error recovery middleware