3.8 KiB
3.8 KiB
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(...)returnsApiProblemError)backend/src/v1/schedule_items/service.py(raisesApiProblemErrorwithproblem_payload(...))backend/src/v1/users/dependencies.py(auth failures mapped toAUTH_UNAUTHORIZED,JWT_VERIFIER_NOT_CONFIGURED)
2) Infra/DB exceptions (caught at boundary and converted)
SQLAlchemyErroris 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.pymay just propagate exceptions. - Service layer usually maps infra failures to stable codes like
*_SERVICE_UNAVAILABLE/*_STORE_UNAVAILABLE.
Examples:
backend/src/v1/todo/repository.pybackend/src/v1/todo/service.pybackend/src/v1/schedule_items/service.py
Error Handling Patterns
Observed patterns:
- Service methods: catch DB/infrastructure errors, rollback when needed, then raise typed API problem.
backend/src/v1/todo/service.pybackend/src/v1/schedule_items/service.py
- Repository methods: log exception context and re-raise.
backend/src/v1/todo/repository.pybackend/src/v1/schedule_items/repository.py
- 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_CASEcodes and keepdetailhuman-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
- Protocol source:
- Do not swallow exceptions with
except ...: log; return defaultin new code.- Project-wide rule from root
AGENTS.md: no error swallowing.
- Project-wide rule from root
- Do not add new service/repository branches that raise
HTTPExceptiondirectly.- Backend target rule in
backend/AGENTS.mdprefersApiProblemErrorin non-router layers.
- Backend target rule in
Existing legacy evidence to be aware of:
backend/src/core/db/base_service.pystill raisesHTTPException(401, "Unauthorized").backend/src/v1/users/dependencies.pyhas fallback branches returningNoneafter 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.pydefinesregister_exception_handlers(...)returning{ "detail": "Internal Server Error" }JSON (non-problem+json); main app currently defines its own handlers inbackend/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
codein all error branches; compliance is currently convention + review driven.