How to Handle Errors with xerrors in Microservices Using Fabrica-Kit

Use the xerrors package in go-pantheon/fabrica-kit to define centralized sentinel errors in kiterrors.go, wrap them with context using factory functions, classify them with IsUnlogErr or IsLogoutError helpers, and convert them to Kratos-compatible API errors before sending responses to clients.

The fabrica-kit repository provides a robust error handling framework designed specifically for Go microservices. By leveraging the xerrors package, developers can maintain consistent error taxonomy across services while preserving rich diagnostic context and enabling clean conversion to client-facing error responses.

Centralizing Error Definitions in kiterrors.go

All domain-specific errors in fabrica-kit are declared as package-level variables in xerrors/kiterrors.go. This centralization ensures that every microservice uses identical sentinel values for classification.

Domain-Specific Sentinels

The file defines lightweight error values using the fabrica-util/errors package, which provides standard Go error handling primitives including errors.Is and Wrapf:

// xerrors/kiterrors.go
var (
    ErrRouteTableNotFound = errors.New("route table not found")
    ErrTunnelNotFound     = errors.New("tunnel not found")
    ErrDBNotFound         = errors.New("db not found")
    // ... additional domain errors
)

These sentinels enable reliable error classification using errors.Is() throughout the codebase.

Factory Functions for Contextual Errors

For scenarios requiring additional diagnostic data, kiterrors.go provides factory functions that embed formatted messages while preserving the original sentinel value:

// xerrors/kiterrors.go
func ErrRouteTableNotFoundFunc(key string) error {
    return errors.WithMessagef(ErrRouteTableNotFound, "key=%s", key)
}

This pattern allows calling code to maintain errors.Is compatibility for programmatic handling while logging human-readable context.

Classifying Errors with Helper Utilities

The xerrors package includes classification utilities to distinguish between expected operational errors and exceptional conditions requiring investigation.

Detecting Unloggable Errors

The IsUnlogErr helper identifies errors that represent expected termination conditions rather than system failures:

// xerrors/kiterrors.go
func IsUnlogErr(err error) bool {
    // Returns true for stop-trigger, EOF, cancellation or logout errors
    return errors.Is(err, xsync.ErrStopByTrigger) || 
           errors.Is(err, context.Canceled) ||
           // ... additional checks
}

Use this function in logging middleware to suppress stack traces for routine disconnections while preserving full error details for unexpected failures.

Identifying Logout Scenarios

The IsLogoutError function, defined in xerrors/logouterrors.go, detects session termination errors:

// xerrors/logouterrors.go
func IsLogoutError(err error) bool {
    return errors.Is(err, ErrLogoutByUser) ||
           errors.Is(err, ErrLogoutBySystem) ||
           errors.Is(err, ErrLogoutByDuplicateLogin)
}

This enables consistent handling of session lifecycle events across HTTP and gRPC handlers.

Converting Internal Errors to API Responses

Microservices must translate internal error states into standardized client responses. The xerrors/apierrors.go file provides Kratos-compatible constructors for this purpose.

Kratos-Compatible Error Constructors

These functions generate *kratos.errors.Error instances with standardized reason codes and HTTP status mappings:

// xerrors/apierrors.go
func APIParamInvalid(message string, a ...any) *errors.Error {
    if len(a) > 0 {
        message = fmt.Sprintf(message, a...)
    }
    return errors.BadRequest("PARAM_INVALID", message)
}

func APINotFound(message string, a ...any) *errors.Error {
    if len(a) > 0 {
        message = fmt.Sprintf(message, a...)
    }
    return errors.NotFound("NOT_FOUND", message)
}

The resulting error objects implement the correct gRPC status codes and HTTP status codes for client consumption.

Real-World Usage Pattern in Redis Route Table

The Redis-backed route table implementation in router/routetable/redis/redis.go demonstrates the complete error handling workflow:

// router/routetable/redis/redis.go
func (r *Redis) Get(ctx context.Context, key string) (string, error) {
    val, err := r.client.Get(ctx, key).Result()
    if errors.Is(err, redis.Nil) {
        // Convert Redis Nil to domain error with context
        return "", xerrors.ErrRouteTableNotFoundFunc(key)
    }
    if err != nil {
        // Wrap unexpected errors with context
        return "", errors.Wrapf(err, "key=%s", key)
    }
    return val, nil
}

When this error reaches an HTTP handler, the service translates it for the client:

// handler/router.go
func GetRouteHandler(c *gin.Context) {
    route, err := svc.FetchRoute(c.Request.Context(), c.Param("key"))
    if err != nil {
        if errors.Is(err, xerrors.ErrRouteTableNotFound) {
            apiErr := xerrors.APINotFound("route %s not found", c.Param("key"))
            c.JSON(int(apiErr.Code), apiErr)
            return
        }
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"route": route})
}

Summary

  • Centralize error definitions in xerrors/kiterrors.go using lightweight sentinels created via fabrica-util/errors.
  • Enrich errors contextually with factory functions like ErrRouteTableNotFoundFunc that preserve sentinel identity while adding diagnostic data.
  • Classify errors programmatically using IsUnlogErr and IsLogoutError to distinguish routine operational events from genuine failures.
  • Convert to API responses with Kratos-compatible constructors in xerrors/apierrors.go for consistent HTTP/gRPC status codes.
  • Maintain errors.Is compatibility throughout the stack to enable reliable error checking without string parsing.

Frequently Asked Questions

What is the xerrors package in fabrica-kit?

The xerrors package is a centralized error handling framework located in the go-pantheon/fabrica-kit repository. It provides sentinel error definitions, contextual wrapping functions, classification utilities, and API conversion helpers specifically designed for microservices architecture.

How do I check if an error is a specific sentinel in fabrica-kit?

Use the standard errors.Is function from Go's standard library against the exported sentinel variables in xerrors/kiterrors.go. For example: if errors.Is(err, xerrors.ErrRouteTableNotFound) { ... }. This works because the factory functions preserve the original sentinel identity when wrapping.

When should I use factory functions versus direct sentinel errors?

Use factory functions like ErrRouteTableNotFoundFunc when you need to attach dynamic context (such as IDs or keys) to the error message while maintaining the ability to check against the static sentinel later. Use direct sentinel errors for static error conditions that require no additional context.

How do I convert fabrica-kit errors to HTTP/gRPC responses?

Import the xerrors/apierrors.go constructors such as APIParamInvalid or APINotFound. These functions return *kratos.errors.Error instances that carry appropriate HTTP status codes and gRPC status codes, allowing your handlers to return standardized responses directly to clients.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →