> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Consistent error responses based on SoException

Learn how to consistently handle errors in APIs and provide clear responses.

## Error Handling Overview

<CardGroup cols={2}>
  <Card title="Consistent Responses" icon="equals">
    Standardized error format Client-friendly
  </Card>

  <Card title="Status Codes" icon="hashtag">
    HTTP status codes RESTful standard
  </Card>

  <Card title="Clear Messages" icon="message">
    Developer-friendly User-friendly
  </Card>

  <Card title="Logging" icon="file-lines">
    Error tracking Debugging support
  </Card>
</CardGroup>

## SoException

Sonamu provides error classes based on `SoException`. When you throw a `SoException`, the framework automatically sends an appropriate HTTP status code and structured error response.

### Basic Usage

```typescript theme={null}
import { NotFoundException } from "sonamu";

class UserModel extends BaseModelClass {
  @api({ httpMethod: "GET" })
  async get(id: number): Promise<User> {
    const rdb = this.getPuri("r");

    const user = await rdb.table("users").where("id", id).first();

    if (!user) {
      throw new NotFoundException("User not found");
    }

    return user;
  }
}
```

### Available Exception Classes

These are the exception classes provided by Sonamu. All inherit from `SoException`.

| Class                          | Status Code | When to Use                                        |
| ------------------------------ | ----------- | -------------------------------------------------- |
| `BadRequestException`          | 400         | Invalid parameters or malformed request            |
| `UnauthorizedException`        | 401         | Authentication required or access denied           |
| `NotFoundException`            | 404         | Accessing a non-existent record                    |
| `InternalServerErrorException` | 500         | Internal processing or external API call error     |
| `ServiceUnavailableException`  | 503         | Processing not possible in current state           |
| `TargetNotFoundException`      | 520         | Processing target does not exist                   |
| `AlreadyProcessedException`    | 541         | Request has already been processed                 |
| `DuplicateRowException`        | 542         | Duplicate request where duplicates are not allowed |

### SoException Constructor

All exception classes share the same constructor signature.

```typescript theme={null}
constructor(message: LocalizedString, payload?: unknown)
```

* `message`: Error message. Supports localized strings (`LocalizedString`).
* `payload`: Additional information. Can pass Zod validation issue arrays, etc.

## Usage Examples

### Various Error Situations

```typescript theme={null}
import {
  BadRequestException,
  UnauthorizedException,
  NotFoundException,
  DuplicateRowException,
} from "sonamu";

class UserModel extends BaseModelClass {
  @api({ httpMethod: "GET" })
  async get(id: number): Promise<User> {
    const rdb = this.getPuri("r");

    const user = await rdb.table("users").where("id", id).first();

    if (!user) {
      throw new NotFoundException("User not found");
    }

    return user;
  }

  @api({ httpMethod: "POST" })
  async create(params: CreateUserParams): Promise<{ userId: number }> {
    const context = Sonamu.getContext();

    // Check authentication
    if (!context.user) {
      throw new UnauthorizedException("Authentication required");
    }

    // Input validation
    if (!params.email) {
      throw new BadRequestException("Email is required");
    }

    const rdb = this.getPuri("r");

    // Check duplicate
    const existing = await rdb.table("users").where("email", params.email).first();

    if (existing) {
      throw new DuplicateRowException("Email already exists");
    }

    const wdb = this.getPuri("w");
    const [user] = await wdb.table("users").insert(params).returning("id");

    return { userId: user.id };
  }
}
```

### Passing Detailed Info via payload

```typescript theme={null}
import { BadRequestException } from "sonamu";

class OrderModel extends BaseModelClass {
  @api({ httpMethod: "POST" })
  async create(params: CreateOrderParams): Promise<{ orderId: number }> {
    // Pass error details via payload
    if (params.quantity <= 0) {
      throw new BadRequestException("Invalid order quantity", {
        field: "quantity",
        value: params.quantity,
        constraint: "must be > 0",
      });
    }

    // ...
  }
}
```

### isSoException Type Guard

```typescript theme={null}
import { isSoException, AlreadyProcessedException } from "sonamu";

class PaymentModel extends BaseModelClass {
  @api({ httpMethod: "POST" })
  async process(paymentId: number): Promise<void> {
    try {
      await this.executePayment(paymentId);
    } catch (error) {
      if (isSoException(error) && error.statusCode === 541) {
        // Already processed payment - ignore
        return;
      }
      throw error;
    }
  }
}
```

## Zod Validation Error Handling

When you pass a Zod issue array to `BadRequestException`'s `payload`, Sonamu's error handler automatically includes validation error details in the response.

### Converting Zod Errors

```typescript theme={null}
import { z } from "zod";
import { BadRequestException } from "sonamu";

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
  password: z.string().min(8),
});

class UserModel extends BaseModelClass {
  @api({ httpMethod: "POST" })
  async create(params: unknown): Promise<{ userId: number }> {
    const result = CreateUserSchema.safeParse(params);

    if (!result.success) {
      throw new BadRequestException("Validation failed", result.error.issues);
    }

    const validated = result.data;

    const wdb = this.getPuri("w");
    const [user] = await wdb.table("users").insert(validated).returning("id");

    return { userId: user.id };
  }
}
```

## Error Response Format

Sonamu's built-in error handler responds in the following format.

### Basic Error Response

```json theme={null}
{
  "name": "NotFoundException",
  "code": null,
  "message": "User not found"
}
```

### Zod Validation Error Response (when issue array is passed as payload)

```json theme={null}
{
  "name": "BadRequestException",
  "code": null,
  "message": "Validation failed (email)",
  "issues": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["email"],
      "message": "Required"
    }
  ]
}
```

## Customizing the Error Handler

Sonamu includes a built-in error handler, but you can set a custom error handler via the `lifecycle.onError` option in `startServer` if needed.

```typescript theme={null}
import { Sonamu, isSoException } from "sonamu";

Sonamu.startServer({
  lifecycle: {
    onError: (error, request, reply) => {
      // Custom error handling logic
      if (isSoException(error)) {
        reply.status(error.statusCode).send({
          error: error.message,
          payload: error.payload,
        });
      } else {
        reply.status(500).send({
          error: "Internal server error",
        });
      }
    },
  },
});
```

## Status Code Reference

### Standard HTTP Status Codes

| Code | Name                  | When to Use                   |
| ---- | --------------------- | ----------------------------- |
| 200  | OK                    | Success (GET, PUT)            |
| 201  | Created               | Resource created (POST)       |
| 204  | No Content            | Success, no response (DELETE) |
| 400  | Bad Request           | Invalid request format        |
| 401  | Unauthorized          | Authentication required       |
| 404  | Not Found             | Resource not found            |
| 500  | Internal Server Error | Internal server error         |
| 503  | Service Unavailable   | Service unavailable           |

### Sonamu Custom Status Codes

| Code | Exception Class             | When to Use                            |
| ---- | --------------------------- | -------------------------------------- |
| 520  | `TargetNotFoundException`   | Processing target does not exist       |
| 541  | `AlreadyProcessedException` | Request already processed              |
| 542  | `DuplicateRowException`     | Duplicate where duplicates not allowed |

## Practical Pattern

```typescript theme={null}
import {
  UnauthorizedException,
  BadRequestException,
  DuplicateRowException,
} from "sonamu";

class UserModel extends BaseModelClass {
  @api({ httpMethod: "POST" })
  async create(params: unknown): Promise<{ userId: number }> {
    const context = Sonamu.getContext();

    // 1. Check authentication
    if (!context.user) {
      throw new UnauthorizedException("Authentication required");
    }

    // 2. Zod validation
    const validated = CreateUserSchema.safeParse(params);
    if (!validated.success) {
      throw new BadRequestException("Validation failed", validated.error.issues);
    }

    const data = validated.data;

    // 3. Business rule validation
    const rdb = this.getPuri("r");
    const existing = await rdb.table("users").where("email", data.email).first();

    if (existing) {
      throw new DuplicateRowException("Email already exists");
    }

    // 4. Create
    const wdb = this.getPuri("w");
    const [user] = await wdb.table("users").insert(data).returning("id");

    return { userId: user.id };
  }
}
```

## Cautions

<Warning>
  **Cautions for error handling**: 1. Never expose sensitive information (stack traces, DB errors,
  etc.) 2. Use appropriate HTTP status codes 3. Provide clear and consistent error messages 4.
  Always log errors 5. Limit detailed information in production
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Automatic Validation" icon="wand-magic-sparkles" href="/en/api-development/validation/automatic-validation">
    Zod-based validation
  </Card>

  <Card title="Custom Validation" icon="code" href="/en/api-development/validation/custom-validation">
    Custom validation logic
  </Card>

  <Card title="@api Decorator" icon="at" href="/en/api-development/creating-apis/api-decorator">
    API basic usage
  </Card>

  <Card title="Context" icon="cube" href="/en/api-development/context">
    SonamuContext
  </Card>
</CardGroup>
