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

> Master microservice error handling with go-pantheon/fabrica-kit and xerrors. Centralize errors, wrap with context, classify, and convert to Kratos API errors.

- Repository: [Pantheon/fabrica-kit](https://github.com/go-pantheon/fabrica-kit)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Use the `xerrors` package in `go-pantheon/fabrica-kit` to define centralized sentinel errors in [`kiterrors.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/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`](https://github.com/go-pantheon/fabrica-kit/blob/main/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`:

```go
// 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`](https://github.com/go-pantheon/fabrica-kit/blob/main/kiterrors.go) provides factory functions that embed formatted messages while preserving the original sentinel value:

```go
// 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:

```go
// 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`](https://github.com/go-pantheon/fabrica-kit/blob/main/xerrors/logouterrors.go), detects session termination errors:

```go
// 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`](https://github.com/go-pantheon/fabrica-kit/blob/main/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:

```go
// 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`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/routetable/redis/redis.go) demonstrates the complete error handling workflow:

```go
// 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:

```go
// 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`](https://github.com/go-pantheon/fabrica-kit/blob/main/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`](https://github.com/go-pantheon/fabrica-kit/blob/main/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`](https://github.com/go-pantheon/fabrica-kit/blob/main/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`](https://github.com/go-pantheon/fabrica-kit/blob/main/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.