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

# @figentra/error: Typed Error Classes for TypeScript

> Throw and catch typed errors consistently across your Figentra app with typed error classes, HTTP status codes, and instanceof narrowing helpers.

Untyped errors are one of the most common sources of bugs in TypeScript applications. When every layer of your stack throws plain `Error` objects with ad-hoc properties, catch blocks have no reliable structure to work with — you end up guessing field names or silencing errors entirely. `@figentra/error` solves this by providing a hierarchy of typed error classes that carry a machine-readable `code`, an HTTP `statusCode`, and optional structured `details`. Every class in the hierarchy extends the native `Error`, so existing tooling — stack traces, `instanceof` checks, serialisation libraries — all work without modification.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @figentra/error
  ```

  ```bash pnpm theme={null}
  pnpm add @figentra/error
  ```

  ```bash yarn theme={null}
  yarn add @figentra/error
  ```
</CodeGroup>

## Built-in Error Classes

### `FigentraError` — base class

All Figentra errors extend `FigentraError`. It adds `code`, `statusCode`, and `details` to the standard `Error` interface. Throw this directly for errors that don't fit a more specific subclass.

```typescript theme={null}
import { FigentraError } from '@figentra/error';

throw new FigentraError('Something went wrong', 'GENERIC_ERROR', 500);
```

```typescript theme={null}
// Type signature
class FigentraError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number,
    public readonly details?: unknown,
  ) {}

  static isFigentraError(e: unknown): e is FigentraError;
}
```

***

### `NotFoundError` — 404

Throw when a requested resource does not exist. Maps to HTTP `404 Not Found`.

```typescript theme={null}
import { NotFoundError } from '@figentra/error';

async function findUser(id: string) {
  const user = await db.users.findById(id);
  if (!user) throw new NotFoundError(`User with id "${id}" not found`);
  return user;
}
```

***

### `ValidationError` — 422

Throw when user-supplied input fails validation. Pass structured field errors in `details` so the caller can surface actionable feedback.

```typescript theme={null}
import { ValidationError } from '@figentra/error';

function validateCreateUser(input: unknown) {
  const errors = runValidation(input);
  if (errors.length > 0) {
    throw new ValidationError('Input validation failed', errors);
  }
}
```

***

### `UnauthorizedError` — 401

Throw when the caller is not authenticated or their credentials are invalid. Maps to HTTP `401 Unauthorized`.

```typescript theme={null}
import { UnauthorizedError } from '@figentra/error';

function requireAuth(token: string | undefined) {
  if (!token || !verifyToken(token)) {
    throw new UnauthorizedError('Valid authentication credentials are required');
  }
}
```

***

### `ConflictError` — 409

Throw when an operation would violate a uniqueness constraint or create a conflicting state. Maps to HTTP `409 Conflict`.

```typescript theme={null}
import { ConflictError } from '@figentra/error';

async function createUser(email: string) {
  const existing = await db.users.findByEmail(email);
  if (existing) {
    throw new ConflictError(`A user with email "${email}" already exists`);
  }
}
```

***

### `InternalError` — 500

Throw for unexpected failures that are not the caller's fault. Maps to HTTP `500 Internal Server Error`. Avoid leaking sensitive details in the `message`; use `details` for internal diagnostic info that your error-reporting middleware can log.

```typescript theme={null}
import { InternalError } from '@figentra/error';

async function processPayment(orderId: string) {
  try {
    return await paymentGateway.charge(orderId);
  } catch (cause) {
    throw new InternalError('Payment processing failed unexpectedly', { orderId, cause });
  }
}
```

## Creating Custom Errors

Extend `FigentraError` to create domain-specific error classes. Pass your `code` and `statusCode` to `super` so the base class serialises correctly.

```typescript theme={null}
import { FigentraError } from '@figentra/error';

export class PaymentFailedError extends FigentraError {
  constructor(message: string, public readonly orderId: string) {
    super(message, 'PAYMENT_FAILED', 402, { orderId });
  }
}

// Usage
throw new PaymentFailedError('Card declined', 'order-abc-123');
```

Custom errors participate fully in `instanceof` checks and in `FigentraError.isFigentraError()` narrowing, so existing error-handling middleware picks them up automatically.

## Catching and Handling

Use `instanceof` to distinguish between specific error types in catch blocks. Always re-throw errors that are not `FigentraError` instances — those are unexpected and should surface as 500s or be handled by a top-level error boundary.

```typescript theme={null}
import { FigentraError, NotFoundError, ValidationError } from '@figentra/error';

async function handleGetUser(id: string) {
  try {
    const user = await userService.findById(id);
    return { status: 200, body: user };
  } catch (error) {
    if (error instanceof NotFoundError) {
      return { status: 404, body: { message: error.message } };
    }

    if (error instanceof ValidationError) {
      return { status: 422, body: { message: error.message, details: error.details } };
    }

    if (error instanceof FigentraError) {
      // Covers all other typed errors (Conflict, Unauthorized, Internal, custom)
      return { status: error.statusCode, body: { code: error.code, message: error.message } };
    }

    throw error; // re-throw unknown errors — do not silently swallow them
  }
}
```

## Error Shape

All Figentra errors serialise to the `ErrorContract` type defined in `@figentra/contracts`. Align your HTTP response bodies to this shape for a consistent API surface across all services.

```typescript theme={null}
interface ErrorContract {
  code: string;
  message: string;
  statusCode: number;
  details?: unknown;
}
```

Use the `.toContract()` helper method on any `FigentraError` instance to produce a plain `ErrorContract` object safe for JSON serialisation:

```typescript theme={null}
app.onError((error, c) => {
  if (FigentraError.isFigentraError(error)) {
    return c.json(error.toContract(), error.statusCode);
  }
  return c.json({ code: 'UNKNOWN_ERROR', message: 'An unexpected error occurred', statusCode: 500 }, 500);
});
```

<Tip>
  Use the static `FigentraError.isFigentraError(e)` guard instead of
  `e instanceof FigentraError` when errors may cross iframe, VM, or module
  boundaries where prototype chains can break. The static guard checks for the
  presence of the `code` and `statusCode` properties rather than relying on
  `instanceof`, making it safe to use in edge runtimes and serialised error
  payloads.

  ```typescript theme={null}
  if (FigentraError.isFigentraError(caught)) {
    // caught is narrowed to FigentraError — .code and .statusCode are available
    logger.error(caught.code, caught.message);
  }
  ```
</Tip>
