---
title: "Use typed domain exceptions · Agent Wranglers"
url: https://agentwranglers.se/playbook/architecture/errors-and-nulls/domain-exceptions
updated: 2026-10-20
licence: "All rights reserved"
---

# Use typed domain exceptions

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Recommended
- Chapter: [Errors and nulls](https://agentwranglers.se/playbook/architecture/errors-and-nulls)
- Rule: Give each kind of failure its own exception type with a clear name and the data needed to handle it. Never throw a bare generic error with only a message.
- How: Create a small family of exceptions, such as `PaymentDeclined` and `OutOfStock`, that extend one base type for the domain. Catch by type, never by matching message text. A bug that no caller can handle, such as a broken invariant, may use the language's built-in errors, such as `TypeError` or `ValueError`. Map each type to one answer at the edge, such as a 409 [problem details](https://agentwranglers.se/playbook/api-design/errors/problem-details) response for `OutOfStock`.
- Check: A linter rule bans throwing the base type. Examples are Ruff's `TRY002`, ESLint `no-restricted-syntax` on `throw new Error`, and a custom PHPStan rule against `throw new \Exception`.

Example:

```python
# Bad: callers must parse the message
raise Exception("card declined")

# Good: callers catch the type, and it carries the data
class PaymentDeclined(ShopError):
    def __init__(self, order_id: OrderId, reason: str):
        super().__init__(f"Payment declined for {order_id}: {reason}")
        self.order_id, self.reason = order_id, reason
```

```ts
// Good: callers catch the type, and it carries the data
class PaymentDeclined extends ShopError {
  constructor(readonly orderId: OrderId, readonly reason: string) {
    super(`Payment declined for ${orderId}: ${reason}`);
  }
}
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers architecture playbook: https://agentwranglers.se/playbook/architecture/errors-and-nulls/domain-exceptions`.

- Previous: [Throw exceptions instead of returning error codes](https://agentwranglers.se/playbook/architecture/errors-and-nulls/exceptions-not-error-codes)
- Next: [Never return null](https://agentwranglers.se/playbook/architecture/errors-and-nulls/never-return-null)
- [Errors and nulls](https://agentwranglers.se/playbook/architecture/errors-and-nulls)
