Summary
The codebase lacks a centralized error handling strategy. CLAUDE.md specifies domain errors should be raised and mapped to HTTP, but no implementation exists.
Current State
- No
osa/core/error.py or domain error types
- No HTTP error mapper
- Exceptions bubble up as 500 Internal Server Errors
Proposed Design
1. Domain Error Hierarchy
# osa/domain/shared/error.py
class DomainError(Exception):
"""Base class for all domain errors."""
def __init__(self, message: str, code: str | None = None):
self.message = message
self.code = code or self.__class__.__name__
super().__init__(message)
class NotFoundError(DomainError):
"""Resource not found."""
pass
class ValidationError(DomainError):
"""Input validation failed."""
def __init__(self, message: str, field: str | None = None):
super().__init__(message, code="VALIDATION_ERROR")
self.field = field
class InvalidStateError(DomainError):
"""Operation not allowed in current state."""
pass
class ConflictError(DomainError):
"""Resource already exists or version conflict."""
pass
class AuthorizationError(DomainError):
"""User not authorized for this operation."""
pass
2. HTTP Error Mapper
# osa/application/api/v1/errors.py
from fastapi import HTTPException
from osa.domain.shared.error import (
DomainError, NotFoundError, ValidationError,
InvalidStateError, ConflictError, AuthorizationError
)
ERROR_STATUS_MAP = {
NotFoundError: 404,
ValidationError: 422,
InvalidStateError: 409,
ConflictError: 409,
AuthorizationError: 403,
}
def map_domain_error(error: DomainError) -> HTTPException:
"""Map domain errors to HTTP exceptions."""
status_code = ERROR_STATUS_MAP.get(type(error), 400)
return HTTPException(
status_code=status_code,
detail={
"code": error.code,
"message": error.message,
"field": getattr(error, "field", None),
}
)
3. Global Exception Handler
# osa/application/api/rest/app.py
@app.exception_handler(DomainError)
async def domain_error_handler(request: Request, exc: DomainError):
http_exc = map_domain_error(exc)
return JSONResponse(
status_code=http_exc.status_code,
content=http_exc.detail,
)
4. Usage in Services
class DepositionService(Service):
async def submit(self, dep_id: DepositionSRN) -> SubmitResult:
dep = await self.repo.get(dep_id)
if dep is None:
raise NotFoundError(f"Deposition {dep_id} not found")
if dep.status != DepositionStatus.DRAFT:
raise InvalidStateError(f"Cannot submit from {dep.status}")
# ...
Files to Create/Modify
Summary
The codebase lacks a centralized error handling strategy. CLAUDE.md specifies domain errors should be raised and mapped to HTTP, but no implementation exists.
Current State
osa/core/error.pyor domain error typesProposed Design
1. Domain Error Hierarchy
2. HTTP Error Mapper
3. Global Exception Handler
4. Usage in Services
Files to Create/Modify
osa/domain/shared/error.py- Domain error hierarchyosa/application/api/v1/errors.py- HTTP error mapperosa/application/api/rest/app.py- Register global handler