# How to Implement Custom Error Handlers with Error Types in Gorig

> Learn to implement custom error handlers with Gorig error types. Map business logic to HTTP responses, ensure consistent JSON, automatic logging, and panic recovery effortlessly.

- Repository: [Jom/gorig](https://github.com/jom-io/gorig)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Gorig provides a structured error-handling framework based on a typed `Error` struct in `utils/errors` that maps business logic failures to HTTP responses via the `apix` package, enabling consistent JSON formatting, automatic logging, and panic recovery.**

The jom-io/gorig repository offers a comprehensive solution to implement custom error handlers with error types that standardize how Go applications communicate failures to API consumers. By leveraging the `Error` struct defined in [`utils/errors/err.go`](https://github.com/jom-io/gorig/blob/main/utils/errors/err.go) alongside response utilities in [`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go), developers can ensure that system crashes, validation failures, and coding errors each trigger appropriate HTTP status codes and uniform JSON payloads.

## Understanding Gorig's Typed Error Architecture

Gorig categorizes every error into one of three distinct **types**: `Coding`, `System`, and `Application`. This classification determines how the framework logs the incident and which HTTP status code it returns to the client.

The central `Error` struct in [`utils/errors/err.go`](https://github.com/jom-io/gorig/blob/main/utils/errors/err.go) carries a `Type` field (string), a `Code` field (string for numeric identifiers), and a `Message`. The `CodeInt()` method parses the string code into an integer, allowing the API layer to override default HTTP statuses when specific business error codes are required.

### Core Source Files and Responsibilities

- **[`utils/errors/err.go`](https://github.com/jom-io/gorig/blob/main/utils/errors/err.go)**: Defines the `Error` struct, type constants, and constructors (`Sys`, `Verify`, `Assert`, `VerifyCode`). This is where you create typed error instances.
- **[`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go)**: Contains `Handle`, `HandleData`, `HandleError`, and `PanicNotify`. These functions translate `*errors.Error` into HTTP responses and manage logging.
- **[`apix/response/response.go`](https://github.com/jom-io/gorig/blob/main/apix/response/response.go)**: Provides low-level JSON rendering helpers (`Success`, `Fail`, `ErrorSystem`) that standardize the response shape.
- **[`httpx/mid.recovery.go`](https://github.com/jom-io/gorig/blob/main/httpx/mid.recovery.go)**: Implements the `Recovery()` Gin middleware that intercepts panics and forwards them to `apix.PanicNotify` as typed errors.

## Defining Custom Error Types

Create reusable error definitions using the constructor functions from `utils/errors`. Each constructor returns `*errors.Error` pre-configured with the appropriate type classification.

```go
import "github.com/jom-io/gorig/utils/errors"

// Application-level error for business rule violations (maps to 4xx responses)
var ErrInsufficientBalance = errors.Verify("balance not enough for the requested operation")

// System-level error for infrastructure failures (maps to 500 responses)
var ErrDatabaseConnection = errors.Sys("cannot connect to database")

// Coding-level error for invalid arguments or assertions
var ErrInvalidUserID = errors.Assert("userID", "value cannot be empty")

// Custom numeric code for specific HTTP status mapping
var ErrResourceNotFound = errors.VerifyCode(404, "resource not found")

```

**Key constructors:**
- **`Sys(message, nativeErr...)`**: Creates `System` type errors for database, network, or file system failures.
- **`Verify(message, nativeErr...)`**: Creates `Application` type errors for validation or business logic violations.
- **`VerifyCode(code, message, nativeErr...)`**: Creates `Application` errors with an explicit numeric code that overrides the default HTTP status.
- **`Assert(key, value)`**: Creates `Coding` type errors for invalid arguments or programming assertions.

## Configuring Global Error Recovery

Before handling specific errors in controllers, register the `Recovery` middleware to capture unhandled panics and convert them into typed errors automatically.

```go
import "github.com/jom-io/gorig/httpx"

func main() {
    // Startup creates the Gin engine and registers Recovery middleware
    httpx.Startup("myService", "8080")
}

```

The `Recovery()` middleware defined in [`httpx/mid.recovery.go`](https://github.com/jom-io/gorig/blob/main/httpx/mid.recovery.go) wraps every HTTP handler. When a panic occurs, it recovers, constructs a `System` type `*errors.Error`, and invokes `apix.PanicNotify`, which logs the stack trace and optionally sends alerts before returning a sanitized 500 response to the client.

## Implementing Error Handlers in Controllers

The `apix` package provides three primary functions to implement custom error handlers in your Gin controllers. Each accepts an `*errors.Error` and produces a consistent JSON response with the shape `{code: <int>, msg: <string>, data: <any>}`.

### Handling Native Errors with HandleError

Use `HandleError` when you need to wrap a standard Go `error` and control the HTTP status code explicitly.

```go
func GetUser(c *gin.Context) {
    user, err := svc.FindUserByID(c.Param("id"))
    if err != nil {
        // Convert native error to System type and return 500
        apix.HandleError(c, http.StatusInternalServerError, nil, errors.Sys(err.Error()))
        return
    }
    apix.HandleData(c, http.StatusOK, user, nil)
}

```

### Routing Typed Errors with Handle

Use `Handle` when you already have an `*errors.Error` (such as the custom types defined earlier) and want the framework to determine the response format based on the error's type.

```go
func CreateOrder(c *gin.Context) {
    if err := svc.ValidateOrder(c.Request.Body); err != nil {
        // err is *errors.Error (e.g., ErrInsufficientBalance)
        // Handle inspects err.Type: Application errors trigger response.Fail()
        apix.Handle(c, http.StatusBadRequest, err)
        return
    }
    
    orderID, err := svc.Create(c)
    if err != nil {
        apix.Handle(c, http.StatusInternalServerError, errors.Sys(err.Error()))
        return
    }
    
    apix.HandleData(c, http.StatusCreated, map[string]string{"id": orderID}, nil)
}

```

### Returning Data with Error Checks

`HandleData` simplifies the success path by automatically returning the provided data when the error parameter is `nil`, while still handling error cases consistently.

```go
func DeleteResource(c *gin.Context) {
    if !isAdmin(c) {
        // Application error with custom code 403
        apix.Handle(c, http.StatusForbidden, errors.VerifyCode(403, "admin privilege required"))
        return
    }
    
    if err := svc.Delete(c.Param("id")); err != nil {
        apix.Handle(c, http.StatusInternalServerError, errors.Sys(err.Error()))
        return
    }
    
    // Empty success payload with 204 status
    apix.Handle(c, http.StatusNoContent, nil)
}

```

## Summary

- **Define** reusable `*errors.Error` instances using constructors in [`utils/errors/err.go`](https://github.com/jom-io/gorig/blob/main/utils/errors/err.go) to categorize failures as `System`, `Application`, or `Coding`.
- **Register** the `Recovery` middleware via `httpx.Startup` to capture panics and convert them to typed errors automatically.
- **Route** errors through `apix.Handle`, `HandleData`, or `HandleError` to ensure consistent JSON formatting and appropriate HTTP status codes.
- **Map** custom business codes to HTTP statuses using `VerifyCode` and the `CodeInt()` method for precise API control.

## Frequently Asked Questions

### What is the difference between System and Application error types in Gorig?

**System** errors represent infrastructure failures like database connection timeouts or file system errors, and they automatically trigger HTTP 500 responses via `response.ErrorSystem`. **Application** errors represent business logic violations such as validation failures or insufficient permissions, and they use `response.Fail` to return 4xx status codes with detailed messages. The distinction ensures that internal failures are logged as warnings while user-facing errors are logged as standard errors.

### How do I map custom error codes to specific HTTP status codes?

Use the `errors.VerifyCode(code, message)` constructor to create an `Application` type error with a numeric code string. When passed to `apix.Handle`, the framework calls `err.CodeInt()` to parse the code and overrides the default HTTP status. For example, `errors.VerifyCode(404, "not found")` will return HTTP 404 even if the default for validation errors is 400.

### Can I use native Go errors with Gorig's custom error handlers?

Yes, but you must wrap native errors using `errors.Sys(err.Error())` or `errors.Verify(err.Error())` before passing them to `apix.Handle` or `HandleError`. The `HandleError` function accepts a native error wrapped in the `*errors.Error` struct, allowing you to maintain Gorig's consistent JSON response format while handling legacy error sources.

### How does panic recovery integrate with custom error types?

The `Recovery` middleware in [`httpx/mid.recovery.go`](https://github.com/jom-io/gorig/blob/main/httpx/mid.recovery.go) catches all panics during request handling and converts them into `System` type `*errors.Error` instances. It then calls `apix.PanicNotify`, which logs the error with stack trace information and returns a generic 500 response to the client without exposing internal details. This ensures that even unhandled runtime errors follow your established error response format.