Files

102 lines
3.8 KiB
Markdown

# Error Handling
> How errors are handled in this project.
---
## Overview
Backend error transport is RFC7807-style `application/problem+json` with stable `code` and optional `params` extensions.
Evidence:
- Error class: `backend/src/core/http/errors.py` (`ApiProblemError`)
- Problem details builder: `backend/src/core/http/response.py`
- Global exception mapping: `backend/src/app.py`
- Error code registry: `docs/protocols/common/http-error-codes.md`
---
## Error Types
### 1) `ApiProblemError` (preferred for business/API errors)
Used to carry `status_code`, `detail`, `code`, `params`.
Examples:
- `backend/src/v1/todo/service.py` (`_todo_error(...)` returns `ApiProblemError`)
- `backend/src/v1/schedule_items/service.py` (raises `ApiProblemError` with `problem_payload(...)`)
- `backend/src/v1/users/dependencies.py` (auth failures mapped to `AUTH_UNAUTHORIZED`, `JWT_VERIFIER_NOT_CONFIGURED`)
### 2) Infra/DB exceptions (caught at boundary and converted)
- `SQLAlchemyError` is commonly caught in service/repository boundaries.
- Many repository methods log and re-raise DB exceptions (for example in todo/schedule_items repositories); generic helpers in `core/db/base_repository.py` may just propagate exceptions.
- Service layer usually maps infra failures to stable codes like `*_SERVICE_UNAVAILABLE` / `*_STORE_UNAVAILABLE`.
Examples:
- `backend/src/v1/todo/repository.py`
- `backend/src/v1/todo/service.py`
- `backend/src/v1/schedule_items/service.py`
---
## Error Handling Patterns
Observed patterns:
1. **Service methods**: catch DB/infrastructure errors, rollback when needed, then raise typed API problem.
- `backend/src/v1/todo/service.py`
- `backend/src/v1/schedule_items/service.py`
2. **Repository methods**: log exception context and re-raise.
- `backend/src/v1/todo/repository.py`
- `backend/src/v1/schedule_items/repository.py`
3. **Global handlers**: convert unhandled errors and framework exceptions into problem+json responses.
- `backend/src/app.py`
---
## API Error Responses
Expected transport:
- Media type: `application/problem+json`
- Fields: RFC7807 core (`type`, `title`, `status`, `detail`, `instance`) + extension fields (`code`, `params`)
Evidence:
- Response builder model in `backend/src/core/http/response.py`
- Exception handlers in `backend/src/app.py`
- Registry and compatibility notes in `docs/protocols/common/http-error-codes.md`
Practical rule:
- For business branches, return stable `UPPER_SNAKE_CASE` codes and keep `detail` human-readable.
---
## Anti-patterns / Forbidden Patterns
- Do not return free-text-only errors for business branches (missing `code`).
- Protocol source: `docs/protocols/common/http-error-codes.md`
- Do not swallow exceptions with `except ...: log; return default` in new code.
- Project-wide rule from root `AGENTS.md`: no error swallowing.
- Do not add new service/repository branches that raise `HTTPException` directly.
- Backend target rule in `backend/AGENTS.md` prefers `ApiProblemError` in non-router layers.
Existing legacy evidence to be aware of:
- `backend/src/core/db/base_service.py` still raises `HTTPException(401, "Unauthorized")`.
- `backend/src/v1/users/dependencies.py` has fallback branches returning `None` after catching exceptions in `_verify_user_with_supabase`.
These are current-state observations, not patterns to copy for new code.
---
## Uncertainties (documented, not invented)
- `backend/src/core/logging/middleware.py` defines `register_exception_handlers(...)` returning `{ "detail": "Internal Server Error" }` JSON (non-problem+json); main app currently defines its own handlers in `backend/src/app.py`. The single enforced runtime path should be clarified before broad refactors.
- There is no automated CI check in this repo yet that verifies every endpoint always includes `code` in all error branches; compliance is currently convention + review driven.