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

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, 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, 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, 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, the PanicNotify function orchestrates the complete panic response:

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 processes these distinctions:

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 validates the entire recovery pipeline:

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 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 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, 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 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 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →