API Error Types in fabrica-kit: A Complete Guide to Structured Error Handling
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. The architecture follows a two-tier approach:
-
Error Constructors – Functions like
APIParamInvalid()orAPINotFound()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. -
Pre-Instantiated Defaults – Variables such as
ErrAPIParamInvalidorErrAPINotFoundhold 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 codePARAM_INVALID. Use this for malformed request bodies or missing required fields. Defined inxerrors/apierrors.go:48-56. -
APIPageParamInvalid(message string, a ...any)– Returns 400 Bad Request with reason codePAGE_PARAM_INVALID. Specific to pagination parameter violations. Defined inxerrors/apierrors.go:58-66. -
APIStatusIllegal(message string, a ...any)– Returns 403 Forbidden with reason codeSTATUS_ILLEGAL. Use when an operation is forbidden due to business logic or state constraints. Defined inxerrors/apierrors.go:38-46. -
APINotFound(message string, a ...any)– Returns 404 Not Found with reason codeNOT_FOUND. Use when a requested resource does not exist. Defined inxerrors/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 codeALREADY_EXISTS. Use when attempting to create a resource that already exists. Defined inxerrors/apierrors.go:78-86. -
APIStateUpdateFailed(message string, a ...any)– Returns 409 Conflict with reason codeSTATE_UPDATE_FAILED. Use when a state transition cannot be performed. Defined inxerrors/apierrors.go:88-96. -
APIDBNoAffected(message string, a ...any)– Returns 409 Conflict with reason codeDB_NO_AFFECTED. Use when an UPDATE or DELETE operation affects zero rows, indicating the resource was modified or deleted concurrently. Defined inxerrors/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 codeSESSION_ILLEGAL. Use for malformed or unauthorized sessions. Defined inxerrors/apierrors.go:98-106. -
APISessionTimeout(message string, a ...any)– Returns 401 Unauthorized with reason codeSESSION_TIMEOUT. Use for expired sessions. Defined inxerrors/apierrors.go:108-116. -
APIAuthFailed(message string, a ...any)– Returns 401 Unauthorized with reason codeAUTH_FAILED. Use for general authentication failures such as bad credentials. Defined inxerrors/apierrors.go:118-126. -
APIPlatformAuthFailed(message string, a ...any)– Returns 401 Unauthorized with reason codePLATFORM_AUTH_FAILED. Use for platform-level authentication problems. Defined inxerrors/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 codeCODEC_FAILED. Use for serialization or deserialization errors. Defined inxerrors/apierrors.go:138-146. -
APIDBFailed(message string, a ...any)– Returns 500 Internal Server Error with reason codeDB_FAILED. Use for generic database errors. Defined inxerrors/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.
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 lines 11-35. These variables reduce allocation overhead and standardize error messages across your application.
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:
ErrAPIStatusIllegalErrAPIParamInvalidErrAPIPageParamInvalidErrAPINotFoundErrAPIAlreadyExistsErrAPIStateUpdateFailedErrAPISessionIllegalErrAPISessionTimeoutErrAPIAuthFailedErrAPIPlatformAuthFailedErrAPICodecFailedErrAPIDBFailedErrAPIDBNoAffected
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.
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:
- Extracts the HTTP status code from the error definition (400, 401, 403, 404, 409, or 500).
- Retrieves the machine-readable reason code (e.g.,
PARAM_INVALID,NOT_FOUND) viaerrors.Reason(err). - Serializes the response as JSON with the following structure:
{
"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
xerrorspackage, 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 likeErrAPIParamInvalidprovide zero-allocation defaults. - The
APIDBNoAffectedconstructor 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →