# Error Classes

Errors are **generated** from command/query documentation. Do not write `lib/errors.ts` manually.

## How it works

1. Write error scenarios in `docs/commands/*.md` as `CODE: Description` pairs
2. Run `erp-kit module generate code -p <path>` to produce `lib/errors.generated.ts`
3. Import generated errors in your command implementations

## Naming convention (in docs)

- Error code in docs: `SCREAMING_SNAKE_CASE` (e.g., `DUPLICATE_SKU`)
- Generated class name: PascalCase + `Error` suffix (e.g., `DuplicateSkuError`)
- Generated error code string: `{MODULE_PREFIX}_{DOC_CODE}` (e.g., `ITEM_MANAGEMENT_DUPLICATE_SKU`)
- Generated via `createDomainError()` with contextual message

The generator automatically prefixes error codes with the module name in `UPPER_SNAKE_CASE` to ensure uniqueness across modules. You only write the short code in docs; the prefix is added at generation time.

## Error category naming rules

Use the **entity name** (not business-domain abbreviations) in error codes. Codes should be self-describing and consistent across modules.

| Category          | Pattern                                 | Doc example                | Generated code example                     |
| ----------------- | --------------------------------------- | -------------------------- | ------------------------------------------ |
| Not found         | `{ENTITY}_NOT_FOUND`                    | `UNIT_NOT_FOUND`           | `PRIMITIVES_UNIT_NOT_FOUND`                |
| Duplicate         | `DUPLICATE_{FIELD}`                     | `DUPLICATE_SKU`            | `ITEM_MANAGEMENT_DUPLICATE_SKU`            |
| Inactive / locked | `{FIELD}_LOCKED` or `{ENTITY}_INACTIVE` | `UOM_LOCKED`               | `PRODUCT_MANAGEMENT_UOM_LOCKED`            |
| Invalid state     | `INVALID_{WHAT}`                        | `INVALID_STATE_TRANSITION` | `USER_MANAGEMENT_INVALID_STATE_TRANSITION` |
| No-op             | `NO_FIELDS_TO_UPDATE`                   | `NO_FIELDS_TO_UPDATE`      | `PRODUCT_MANAGEMENT_NO_FIELDS_TO_UPDATE`   |

### Cross-module foreign key errors

When a command validates an entity from another module (e.g., item-management checking that a Unit from primitives exists), use `{ENTITY}_NOT_FOUND` — the same pattern as same-module not-found errors. Each module gets its own prefixed code (e.g., both `PRIMITIVES_UNIT_NOT_FOUND` and `ITEM_MANAGEMENT_UNIT_NOT_FOUND` can coexist).

### Idempotent error policy

If a command's outcome is already the current state (e.g., deactivating an already-inactive entity), return `ok()` — not an error. Only return errors for genuinely invalid operations.
