# How to Handle Errors and Exceptions in PentAGI: A Layered Architecture Guide

> Master error and exception handling in PentAGI with our layered architecture guide. Discover how wrapped errors, HTTP responses, and recovery middleware ensure robust applications.

- Repository: [VXControl/pentagi](https://github.com/vxcontrol/pentagi)
- Tags: how-to-guide
- Published: 2026-03-21

---

**TLDR:** PentAGI handles errors through a layered architecture where low-level code returns wrapped Go errors using `fmt.Errorf` with `%w`, the service layer translates these into centralized HTTP error responses via `response.Error`, and Gin's `Recovery` middleware catches panics to prevent server crashes while maintaining structured logging throughout.

Error handling in PentAGI (vxcontrol/pentagi) follows a systematic, production-ready approach that separates low-level operations from API responses. This guide examines the actual implementation patterns found in the repository, demonstrating how the backend maintains consistency across database calls, external service integrations, and HTTP endpoints using a centralized error model.

## Understanding PentAGI's Layered Error Handling Architecture

PentAGI implements a four-tier error handling strategy that propagates errors from raw Go errors to structured HTTP responses, ensuring each layer has a single responsibility.

### Low-Level Error Wrapping with Context

At the foundation, functions in tools and database packages return standard Go errors with context using `fmt.Errorf` and the `%w` verb. This preserves the original error while adding domain-specific context for upstream inspection.

In [`backend/pkg/tools/traversaal.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/traversaal.go), external API failures are wrapped as follows:

```go
return "", fmt.Errorf("failed to search in traversaal: %w", err)

```

This pattern allows service handlers to use `errors.Is()` or `errors.As()` to check for specific underlying error types.

### Service Layer Error Translation

REST and GraphQL handlers in the service layer use `logger.FromContext(...).WithError(err)` to capture structured logs before selecting the appropriate HTTP error code. The `response.Error` function converts internal errors into standardized API responses using the centralized error definitions.

From [`backend/pkg/server/services/users.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/services/users.go):

```go
logger.FromContext(c).WithError(err).Errorf("error finding current user")
response.Error(c, response.ErrInternal, err)

```

### Centralized HTTP Error Definitions

All API-level errors are defined in [`backend/pkg/server/response/errors.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/response/errors.go). Each `*response.HttpError` contains an HTTP status code, a machine-readable code string, and a human-readable message, ensuring consistent responses across all endpoints.

## Implementing Error Handling in PentAGI Services

When handling errors in service methods, follow the pattern used in the user service implementation. First check for specific error types like `gorm.ErrRecordNotFound`, then fall back to generic internal errors.

Complete example from [`backend/pkg/server/services/users.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/services/users.go):

```go
func (s *UserService) GetCurrentUser(c *gin.Context) {
    // Call lower-level code (DB, external service, etc.)
    if err := s.db.Take(&user, "id = ?", uid).Error; err != nil {
        // Log with structured context
        logger.FromContext(c).WithError(err).Errorf("error finding current user")

        // Choose the appropriate HttpError
        if errors.Is(err, gorm.ErrRecordNotFound) {
            response.Error(c, response.ErrUsersNotFound, err) // 404
        } else {
            response.Error(c, response.ErrInternal, err)        // 500
        }
        return
    }

    // On success, write a success payload
    response.Success(c, http.StatusOK, user)
}

```

This pattern ensures that database-specific errors are translated into appropriate HTTP status codes (404 for missing records, 500 for system failures) while maintaining the original error context for logging.

## Logging and Observability in PentAGI Error Handling

Structured logging ensures errors are traceable across distributed systems. PentAGI uses `logger.FromContext` to extract request-scoped loggers and attaches error details using `WithError`, enabling correlation between HTTP requests and backend failures.

Example from [`backend/pkg/tools/traversaal.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/traversaal.go) showing contextual fields:

```go
logger.FromContext(c).
    WithFields(logrus.Fields{
        "tool": "traversaal",
        "query": action.Query,
    }).
    WithError(err).
    Error("failed to search in traversaal")

```

This pattern creates searchable log entries containing the tool name, query parameters, and the original error message, enabling rapid debugging of external service failures in log aggregation systems like Loki or ELK.

## Handling Panics and Unexpected Errors in PentAGI

Despite careful error handling, panics can occur from unexpected runtime conditions. PentAGI uses Gin's `Recovery` middleware to catch any panic that bubbles up to the HTTP layer, preventing the entire server from crashing.

Configuration in [`backend/pkg/server/router.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/router.go):

```go
router.Use(gin.Recovery())

```

This middleware intercepts panics, logs the stack trace using the configured logger, and returns a generic 500 JSON response to the client. This ensures high availability even when unexpected runtime errors occur in production.

## Summary

- **Layered architecture**: Low-level code returns wrapped Go errors using `fmt.Errorf` with `%w`, while the service layer translates these into HTTP error responses using `response.Error`.
- **Centralized error definitions**: All API errors are defined in [`backend/pkg/server/response/errors.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/response/errors.go) with consistent status codes and machine-readable codes.
- **Structured logging**: Use `logger.FromContext(c).WithError(err)` to capture error context and enable distributed tracing.
- **Panic recovery**: Gin's `Recovery` middleware in [`backend/pkg/server/router.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/router.go) ensures panics don't crash the server.
- **Error inspection**: Use `errors.Is()` to check for specific error types like `gorm.ErrRecordNotFound` when mapping to HTTP status codes.

## Frequently Asked Questions

### How does PentAGI wrap errors from external services?

PentAGI uses Go's standard error wrapping with `fmt.Errorf` and the `%w` verb to preserve the original error context. For example, in [`backend/pkg/tools/traversaal.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/tools/traversaal.go), external API failures are wrapped as `fmt.Errorf("failed to search in traversaal: %w", err)`, allowing upstream callers to inspect the error chain using `errors.Is` or `errors.As` to detect specific failure modes like network timeouts or authentication failures.

### What is the role of the HttpError struct in PentAGI?

The `HttpError` struct, defined in [`backend/pkg/server/response/errors.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/response/errors.go), serves as the centralized error model for all API responses. It encapsulates an HTTP status code, a machine-readable error code (e.g., "Users.NotFound"), and a human-readable message. This ensures that every endpoint returns a consistent JSON schema containing `status`, `code`, and `msg` fields, allowing API clients to handle errors predictably regardless of which service handler generated the failure.

### How does PentAGI handle database record not found errors?

In service handlers like [`backend/pkg/server/services/users.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/services/users.go), database errors are checked using `errors.Is(err, gorm.ErrRecordNotFound)`. When a record is not found, the service logs the error using structured logging and returns `response.ErrUsersNotFound`, which maps to HTTP 404. For other database errors (connection failures, constraint violations), it returns `response.ErrInternal` (HTTP 500), ensuring clients receive semantically appropriate status codes while the server maintains detailed error logs for debugging.

### Can I add custom error codes to PentAGI?

Yes, you can add custom error codes by defining new `*HttpError` instances in [`backend/pkg/server/response/errors.go`](https://github.com/vxcontrol/pentagi/blob/main/backend/pkg/server/response/errors.go) using the `NewHttpError` constructor. Specify the HTTP status code, a unique machine-readable code string (using dot notation like "External.ServiceUnavailable"), and a descriptive message. Once defined, import the error variable in your service handler and use `response.Error(c, yourCustomError, originalErr)` to return consistent, structured error responses that match PentAGI's existing API contract.