# How Gorig Handles Panics and Errors in HTTP Handlers: A Complete Guide

> Learn how Gorig handles panics and errors in HTTP handlers. Discover its middleware pipeline for recovery, structured logging, and appropriate error responses.

- Repository: [Jom/gorig](https://github.com/jom-io/gorig)
- Tags: deep-dive
- Published: 2026-03-04

---

**Gorig handles panics and errors in HTTP handlers through a layered middleware pipeline that catches panics via Gin's CustomRecovery, converts them to structured logs and alerts, and differentiates between system-level failures and application-level errors to return appropriate HTTP responses.**

The `jom-io/gorig` framework provides a robust error-handling strategy for Go web applications built on the Gin framework. By wiring specialized middleware components during server initialization, Gorig ensures that every panic is intercepted, logged, and alerted while maintaining a consistent JSON error response format for clients.

## The Middleware Pipeline for Panic and Error Recovery

When the HTTP server initializes in [`httpx/serv.go`](https://github.com/jom-io/gorig/blob/main/httpx/serv.go), Gorig registers three critical middleware components in a specific order to create a defensive perimeter around all route handlers.

### Recovery Middleware: The First Line of Defense

The `Recovery()` middleware is registered first in the Gin engine, ensuring it wraps all subsequent handlers and middleware. Located in [`httpx/mid.recovery.go`](https://github.com/jom-io/gorig/blob/main/httpx/mid.recovery.go), this component uses `gin.CustomRecovery` to intercept any panic that bubbles up from the request handling chain.

When a panic occurs, the middleware immediately invokes `logger.Error` to create a structured log entry and delegates to `apix.PanicNotify` for comprehensive error processing.

### Logger Middleware: Correlation and Observability

The `Logger()` middleware, defined in [`httpx/mid.logger.go`](https://github.com/jom-io/gorig/blob/main/httpx/mid.logger.go), injects a trace ID into the Gin context at the start of each request. This trace ID propagates through the entire request lifecycle, ensuring that panic logs, error responses, and external alerts can be correlated to specific HTTP requests.

The middleware writes "IN" logs when requests arrive and "OUT" logs when responses are sent, creating a complete audit trail regardless of whether the handler panics or returns normally.

## How Gorig Handles Panics in HTTP Handlers

When a handler triggers a panic—whether from a nil pointer dereference, an out-of-bounds slice access, or an explicit `panic()` call—Gorig executes a structured recovery flow that prioritizes observability and operational alerting.

### The Panic Recovery Flow

1. **Interception**: The `Recovery` middleware catches the panic via `gin.CustomRecovery`.
2. **Logging**: The error is logged with structured fields using `logger.Error(c, "gin recovery", zap.Any("err", err))`.
3. **Notification**: Control passes to `apix.PanicNotify(c, err)` for comprehensive processing.

Inside [`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go), the `PanicNotify` function orchestrates the complete panic response:

```go
func PanicNotify(ctx *gin.Context, err interface{}) {
    if err == nil { return }
    response.ErrorSystem(ctx, GetTraceID(ctx), GetTraceID(ctx))
    debug.PrintStack()
    log := fmt.Sprintf("TraceID: %s,\nPanic: %v, \nRequest: %v,  \nStack: %s",
        GetTraceID(ctx), err, ctx.Request, string(debug.Stack()))
    logger.DPanic(ctx, log)                     // structured panic log
    go dingding.PanicNotifyDefault(log)        // async DingDing alert
}

```

### Operational Outcomes of Panic Handling

Each panic results in four distinct operational actions:

- **Client Response**: The HTTP client receives a generic 500 Internal Server Error with a JSON payload containing `"error"` and the trace ID, preventing information leakage about internal system state.
- **Developer Visibility**: The stack trace is printed to stdout via `debug.PrintStack()` for immediate debugging during development.
- **Structured Logging**: A detailed log entry containing the trace ID, panic value, full request details, and complete stack trace is recorded via `logger.DPanic`.
- **Alerting**: An asynchronous notification is dispatched to DingDing (or other configured alerting channels) via `dingding.PanicNotifyDefault`, ensuring operations teams are immediately aware of system instability.

## Handling Application Errors vs System Errors

Beyond unhandled panics, Gorig distinguishes between **system errors** (infrastructure failures) and **application errors** (business rule violations) to provide appropriate responses and alerting levels.

The `HandleError` function in [`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go) processes these distinctions:

```go
func HandleError(ctx *gin.Context, code int, data *interface{}, err *errors.Error) {
    if err.Type == errors.System {
        logger.Warn(ctx, err.Error())
        response.ErrorSystem(ctx, GetTraceID(ctx), GetTraceID(ctx))
        // same DingDing flow as panic, but for system errors
    }
    if err.Type == errors.Application {
        logger.Error(ctx, err.Error())
        response.Fail(ctx, code, err.Message, data)
    }
}

```

### System Error Handling

When a handler returns an error with `err.Type == errors.System` (such as database connection failures or cache timeouts), Gorig treats it similarly to a panic:

- Returns a generic 500 error to the client via `response.ErrorSystem`.
- Logs a warning with `logger.Warn`.
- Triggers the same DingDing alerting pipeline used for panics, ensuring infrastructure issues receive immediate attention.

### Application Error Handling

For business logic failures (`err.Type == errors.Application`), such as validation failures or authorization denials:

- Returns a specific HTTP status code (typically 400 or 403) with a descriptive message via `response.Fail`.
- Logs an error entry with `logger.Error` for debugging purposes.
- Does **not** trigger external alerting, as these represent expected failure modes rather than system instability.

Higher-level helpers such as `Handle`, `HandleData`, and `SendError` wrap this logic and are used throughout the codebase to ensure consistent error response formatting.

## Integration Testing for Panic Recovery

The framework includes comprehensive tests to verify panic handling behavior. The `TestRecoveryMiddleware` test in [`test/gin_mid_test.go`](https://github.com/jom-io/gorig/blob/main/test/gin_mid_test.go) validates the entire recovery pipeline:

```go
func TestRecoveryMiddleware(t *testing.T) {
    router := gin.New()
    router.Use(httpx.Recovery())
    router.GET("/panic", func(c *gin.Context) {
        panic("test panic")
    })
    
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/panic", nil)
    router.ServeHTTP(w, req)
    
    assert.Equal(t, 500, w.Code)
    assert.Contains(t, w.Body.String(), "error")
}

```

This test confirms that panics are converted to 500 Internal Server Error responses with JSON error payloads, validating the production behavior of the `Recovery` middleware.

## Summary

Gorig implements a comprehensive, multi-layered strategy for handling panics and errors in HTTP handlers:

- **Defensive Middleware Architecture**: The `Recovery` middleware is registered first in the Gin engine to catch all panics, while the `Logger` middleware injects trace IDs for correlation across logs and alerts.
- **Structured Panic Recovery**: Panics trigger a consistent flow in [`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go) that returns generic 500 errors to clients, prints stack traces for developers, writes structured logs with full context, and sends asynchronous DingDing alerts to operations teams.
- **Error Type Differentiation**: System errors (infrastructure failures) receive the same alerting treatment as panics, while application errors (business logic failures) return specific HTTP status codes without triggering external alerts.
- **Observable by Design**: Every error path includes trace ID propagation, structured logging via `zap`, and integration tests that verify 500 responses for panics.

## Frequently Asked Questions

### What happens when a handler panics in Gorig?

When a handler panics, the `Recovery` middleware in [`httpx/mid.recovery.go`](https://github.com/jom-io/gorig/blob/main/httpx/mid.recovery.go) intercepts the panic via `gin.CustomRecovery`. It logs the error using `logger.Error` and delegates to `apix.PanicNotify`, which returns a 500 Internal Server Error to the client, prints the stack trace, writes a structured log entry with the trace ID and request details, and sends an asynchronous DingDing alert.

### How does Gorig distinguish between system errors and application errors?

Gorig uses the `*utils/errors.Error` type which includes a `Type` field. In [`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go), the `HandleError` function checks `err.Type`: if it equals `errors.System`, the error is treated like a panic with a generic 500 response and DingDing alerting; if it equals `errors.Application`, the function returns a specific HTTP status code with a descriptive message via `response.Fail` without triggering external alerts.

### Where is the panic recovery middleware registered in the Gorig framework?

The panic recovery middleware is registered in [`httpx/serv.go`](https://github.com/jom-io/gorig/blob/main/httpx/serv.go) within the `init()` function. It is explicitly added as the **first** middleware to the Gin engine using `engine.Use(httpx.Recovery())`, ensuring it wraps all subsequent handlers and middleware to catch any panics that occur during request processing.

### What information is included in panic alerts sent to DingDing?

The DingDing alert generated by `dingding.PanicNotifyDefault` in [`apix/handle.go`](https://github.com/jom-io/gorig/blob/main/apix/handle.go) includes the trace ID for request correlation, the panic value (error message), the full HTTP request details, and the complete stack trace captured via `debug.Stack()`. This information is formatted into a structured log message and sent asynchronously to ensure the HTTP response is not delayed by the alerting process.