# API Error Types in fabrica-kit: A Complete Guide to Structured Error Handling

> Understand fabrica-kit API error types for structured error handling in Go microservices. Learn to generate Kratos-compatible errors with HTTP status and reason codes for consistent responses.

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

---

**The `fabrica-kit` `xerrors` package provides centralized API error constructors that generate Kratos-compatible errors with specific HTTP status codes and machine-readable reason codes, enabling consistent error responses across Go microservices.**

The `go-pantheon/fabrica-kit` repository provides a robust toolkit for building Go microservices, with its `xerrors` package serving as the central hub for API-level error handling. Understanding the available **API error types in fabrica-kit** is essential for developers who want to implement consistent, structured error responses that integrate seamlessly with the Kratos framework.

## Overview of fabrica-kit Error Architecture

All API errors in `fabrica-kit` are defined in [`xerrors/apierrors.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xerrors/apierrors.go). The architecture follows a two-tier approach:

1. **Error Constructors** – Functions like `APIParamInvalid()` or `APINotFound()` accept a format string and variadic arguments, allowing you to create contextual error messages while preserving the standard HTTP status code and machine-readable **reason code**.

2. **Pre-Instantiated Defaults** – Variables such as `ErrAPIParamInvalid` or `ErrAPINotFound` hold default instances of these errors. Use these when you do not need to customize the error message.

Every error created through these constructors returns a `*errors.Error` type from the Kratos framework, ensuring automatic HTTP response serialization and status code mapping.

## Complete List of API Error Types in fabrica-kit

The `xerrors` package provides 16 distinct error constructors covering client errors (4xx), authentication failures (401), and internal server errors (500).

### Client Errors (4xx)

These errors indicate problems with the request itself.

- **`APIParamInvalid(message string, a ...any)`** – Returns **400 Bad Request** with reason code `PARAM_INVALID`. Use this for malformed request bodies or missing required fields. Defined in `xerrors/apierrors.go:48-56`.

- **`APIPageParamInvalid(message string, a ...any)`** – Returns **400 Bad Request** with reason code `PAGE_PARAM_INVALID`. Specific to pagination parameter violations. Defined in `xerrors/apierrors.go:58-66`.

- **`APIStatusIllegal(message string, a ...any)`** – Returns **403 Forbidden** with reason code `STATUS_ILLEGAL`. Use when an operation is forbidden due to business logic or state constraints. Defined in `xerrors/apierrors.go:38-46`.

- **`APINotFound(message string, a ...any)`** – Returns **404 Not Found** with reason code `NOT_FOUND`. Use when a requested resource does not exist. Defined in `xerrors/apierrors.go:68-76`.

### Conflict Errors (409)

These errors indicate resource state conflicts that prevent the operation from completing.

- **`APIAlreadyExists(message string, a ...any)`** – Returns **409 Conflict** with reason code `ALREADY_EXISTS`. Use when attempting to create a resource that already exists. Defined in `xerrors/apierrors.go:78-86`.

- **`APIStateUpdateFailed(message string, a ...any)`** – Returns **409 Conflict** with reason code `STATE_UPDATE_FAILED`. Use when a state transition cannot be performed. Defined in `xerrors/apierrors.go:88-96`.

- **`APIDBNoAffected(message string, a ...any)`** – Returns **409 Conflict** with reason code `DB_NO_AFFECTED`. Use when an UPDATE or DELETE operation affects zero rows, indicating the resource was modified or deleted concurrently. Defined in `xerrors/apierrors.go:158-166`.

### Authentication Errors (401)

These errors indicate failures in authentication or session management.

- **`APISessionIllegal(message string, a ...any)`** – Returns **401 Unauthorized** with reason code `SESSION_ILLEGAL`. Use for malformed or unauthorized sessions. Defined in `xerrors/apierrors.go:98-106`.

- **`APISessionTimeout(message string, a ...any)`** – Returns **401 Unauthorized** with reason code `SESSION_TIMEOUT`. Use for expired sessions. Defined in `xerrors/apierrors.go:108-116`.

- **`APIAuthFailed(message string, a ...any)`** – Returns **401 Unauthorized** with reason code `AUTH_FAILED`. Use for general authentication failures such as bad credentials. Defined in `xerrors/apierrors.go:118-126`.

- **`APIPlatformAuthFailed(message string, a ...any)`** – Returns **401 Unauthorized** with reason code `PLATFORM_AUTH_FAILED`. Use for platform-level authentication problems. Defined in `xerrors/apierrors.go:128-136`.

### Internal Server Errors (500)

These errors indicate infrastructure or unrecoverable application failures.

- **`APICodecFailed(message string, a ...any)`** – Returns **500 Internal Server Error** with reason code `CODEC_FAILED`. Use for serialization or deserialization errors. Defined in `xerrors/apierrors.go:138-146`.

- **`APIDBFailed(message string, a ...any)`** – Returns **500 Internal Server Error** with reason code `DB_FAILED`. Use for generic database errors. Defined in `xerrors/apierrors.go:148-156`.

## How to Use fabrica-kit API Error Types

### Using Error Constructors with Custom Messages

When you need to provide context-specific error messages, call the constructor functions directly from your service layer. These functions accept `printf`-style formatting arguments.

```go
package user

import (
	"context"
	"github.com/go-pantheon/fabrica-kit/xerrors"
)

func (s *Service) GetUser(ctx context.Context, id string) (*User, error) {
	if id == "" {
		// Returns 400 Bad Request with reason PARAM_INVALID
		return nil, xerrors.APIParamInvalid("user id is required, got empty string")
	}
	
	user, err := s.repo.Find(ctx, id)
	if err != nil {
		return nil, xerrors.APIDBFailed("database query failed for user %s: %v", id, err)
	}
	
	if user == nil {
		// Returns 404 Not Found with reason NOT_FOUND
		return nil, xerrors.APINotFound("user with id %s does not exist", id)
	}
	
	return user, nil
}

```

### Using Pre-Instantiated Default Errors

For common validation failures where a generic message suffices, use the package-level variables defined in [`xerrors/apierrors.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xerrors/apierrors.go) lines 11-35. These variables reduce allocation overhead and standardize error messages across your application.

```go
func createHandler(c *gin.Context) {
	var req CreateReq
	if err := c.BindJSON(&req); err != nil {
		// Uses default message "default param invalid"
		c.Error(xerrors.ErrAPIParamInvalid)
		return
	}
	// Business logic...
}

```

Available pre-instantiated errors include:
- `ErrAPIStatusIllegal`
- `ErrAPIParamInvalid`
- `ErrAPIPageParamInvalid`
- `ErrAPINotFound`
- `ErrAPIAlreadyExists`
- `ErrAPIStateUpdateFailed`
- `ErrAPISessionIllegal`
- `ErrAPISessionTimeout`
- `ErrAPIAuthFailed`
- `ErrAPIPlatformAuthFailed`
- `ErrAPICodecFailed`
- `ErrAPIDBFailed`
- `ErrAPIDBNoAffected`

### Handling Database-Specific Conflicts

When updating resources, detect zero-row updates to prevent silent failures or race conditions. Wrap the low-level `ErrDBRecordNotAffected` (from `xerrors/kiterrors.go:45-47`) with the appropriate API error constructor.

```go
func (s *Service) UpdateProfile(ctx context.Context, p *Profile) error {
	affected, err := s.repo.Update(ctx, p)
	if err != nil {
		return xerrors.APIDBFailed("update failed: %w", err)
	}
	
	if affected == 0 {
		// Returns 409 Conflict with reason DB_NO_AFFECTED
		return xerrors.APIDBNoAffected("profile %s was modified or deleted concurrently", p.ID)
	}
	
	return nil
}

```

## Kratos HTTP Response Integration

`fabrica-kit` leverages the Kratos framework's built-in error handling middleware. When you return any error created via the `xerrors` constructors, Kratos automatically:

1. Extracts the HTTP status code from the error definition (400, 401, 403, 404, 409, or 500).
2. Retrieves the machine-readable **reason code** (e.g., `PARAM_INVALID`, `NOT_FOUND`) via `errors.Reason(err)`.
3. Serializes the response as JSON with the following structure:

```json
{
  "code": 409,
  "reason": "ALREADY_EXISTS",
  "message": "user with email user@example.com already exists"
}

```

This integration eliminates the need for manual HTTP response construction in your handlers. Simply return the appropriate `xerrors` type, and the framework ensures consistent, typed error responses across all API endpoints.

## Summary

- **fabrica-kit** centralizes API error handling in the `xerrors` package, providing 16+ typed error constructors.
- Each error type maps to a specific HTTP status code (400, 401, 403, 404, 409, 500) and machine-readable reason code.
- **Constructors** like `APIParamInvalid()` allow custom messages with formatting, while **pre-instantiated variables** like `ErrAPIParamInvalid` provide zero-allocation defaults.
- The `APIDBNoAffected` constructor specifically handles database race conditions by returning HTTP 409 Conflict when updates affect zero rows.
- Kratos framework integration ensures automatic JSON serialization and proper HTTP status code mapping without manual response handling.

## Frequently Asked Questions

### How do I choose between using a constructor and a pre-instantiated error variable?

Use **pre-instantiated variables** (e.g., `xerrors.ErrAPIParamInvalid`) when the default error message is sufficient for the scenario, as they avoid memory allocation and ensure message consistency across your application. Use **constructors** (e.g., `xerrors.APIParamInvalid("field %s is required", fieldName)`) when you need to provide context-specific details that help clients debug the exact cause of the failure.

### What is the difference between APIDBFailed and APIDBNoAffected?

`APIDBFailed` represents generic database infrastructure failures (connection errors, query timeouts, syntax errors) and returns **HTTP 500 Internal Server Error** with reason code `DB_FAILED`. `APIDBNoAffected` specifically indicates that an UPDATE or DELETE operation completed successfully but modified zero rows, typically due to concurrent modification or deletion, and returns **HTTP 409 Conflict** with reason code `DB_NO_AFFECTED`.

### How can I customize the error message while keeping the standard HTTP status code?

Call the error constructor functions with your custom message using `printf`-style formatting. For example, `xerrors.APINotFound("user %s not found in tenant %s", userID, tenantID)` will return a **404 Not Found** response with reason code `NOT_FOUND`, but the message field in the JSON response will contain your specific text rather than the generic default.

### Are these errors compatible with standard Go error wrapping?

Yes, the errors returned by `fabrica-kit` constructors implement the standard Go `error` interface and work with `errors.Is()` and `errors.As()`. However, for Kratos to properly serialize the HTTP response, you should return the error directly rather than wrapping it with `fmt.Errorf("%w", err)`, as excessive wrapping may obscure the reason code and HTTP status metadata that Kratos extracts from the error structure.