# Custom Exception Hierarchy and Error Handling in the RomM Backend

> Discover RomM's custom exception hierarchy and error handling. Learn how domain-specific exceptions and automatic HTTP status code mapping ensure consistent logging and effective error management in the backend.

- Repository: [The RomM Project/romm](https://github.com/rommapp/romm)
- Tags: internals
- Published: 2026-07-06

---

**RomM implements a flat, domain-specific exception hierarchy where custom exceptions inherit directly from Python's `Exception` class, with specialized modules for filesystem errors, API endpoints, authentication, and background tasks to ensure consistent logging and automatic HTTP status code mapping.**

The RomM backend uses a strategic approach to error management through a custom exception hierarchy designed to separate domain concerns and streamline FastAPI HTTP responses. Located in the `backend/exceptions/` directory, these classes handle everything from filesystem scan failures to database lookup errors, providing clear logging and user-friendly error messages while maintaining a deliberately flat inheritance structure.

## Filesystem Exceptions in RomM

Filesystem errors reside in [`backend/exceptions/fs_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/fs_exceptions.py) and handle problems encountered while scanning the RomM folder structure or accessing platform and ROM data on disk. These exceptions provide human-readable messages that direct users to documentation or explain exactly what resource is missing.

The `FolderStructureNotMatchException` class demonstrates the pattern used for scan failures:

```python

# backend/exceptions/fs_exceptions.py

class FolderStructureNotMatchException(Exception):
    def __init__(self):
        self.message = (
            f"Platforms not found. "
            "Check RomM folder structure here: https://docs.romm.app/latest/Getting-Started/Folder-Structure/ for more details"
        )
        super().__init__(self.message)

```

Additional filesystem exceptions include `PlatformNotFoundException`, `RomsNotFoundException`, and `RomAlreadyExistsException`. These are raised by helper utilities like `fs_platform_handler.get_platforms()` and caught in socket endpoints to emit error messages via WebSocket:

```python

# backend/endpoints/sockets/scan.py

try:
    fs_platforms: list[str] = await fs_platform_handler.get_platforms()
except FolderStructureNotMatchException as e:
    log.error(e)
    await socket_manager.emit("scan:done_ko", e.message)
    return scan_stats

```

## Endpoint Exceptions and Automatic HTTP Response Mapping

API-level exceptions in [`backend/exceptions/endpoint_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/endpoint_exceptions.py) embed `HTTPException` instantiation directly within their constructors. This design ensures that raising the custom exception automatically triggers the appropriate FastAPI HTTP response without requiring explicit exception handling in route functions.

The `PlatformNotFoundInDatabaseException` illustrates this pattern:

```python

# backend/exceptions/endpoint_exceptions.py

class PlatformNotFoundInDatabaseException(Exception):
    def __init__(self, id):
        self.message = f"Platform with id '{id}' not found"
        super().__init__(self.message)
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=self.message)

```

When a database lookup fails, the endpoint code simply raises the exception, and FastAPI automatically returns a JSON payload:

```json
{
  "detail": "Platform with id '42' not found"
}

```

Other endpoint exceptions follow this same scheme, including `RomNotFoundInDatabaseException` and `CollectionPermissionError`. For critical errors such as duplicate collection creation, the exception logs a critical message before raising the HTTP exception:

```python
log.critical(self.message)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=self.message)

```

## Authentication and Pre-Instantiated Exceptions

Authentication errors in [`backend/exceptions/auth_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/auth_exceptions.py) take a different approach by defining pre-instantiated `HTTPException` objects rather than custom classes. This pattern allows for immediate raising without instantiation overhead.

```python

# backend/exceptions/auth_exceptions.py

AuthCredentialsException = HTTPException(
    status_code=status.HTTP_401_UNAUTHORIZED,
    detail="Incorrect username or password",
)

```

Routes use these directly when validation fails:

```python
if not valid:
    raise AuthCredentialsException

```

Similarly, `OAuthCredentialsException` provides specific error details for OAuth-related failures.

## Task, Configuration, and Validation Errors

Background job errors are handled by `SchedulerException` in [`backend/exceptions/task_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/task_exceptions.py), which is raised by Redis-RQ workers when tasks encounter problems. These exceptions are caught by the task runner, logged, and marked as failed without generating HTTP responses since they occur outside the request cycle.

Configuration errors use `ConfigNotWritableException` in [`backend/exceptions/config_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/config_exceptions.py) when the application cannot persist settings to the `.env` file due to permission issues.

Input validation errors are defined in [`backend/utils/validation.py`](https://github.com/rommapp/romm/blob/main/backend/utils/validation.py) as `ValidationError`, which is caught by FastAPI dependencies and converted into `422 Unprocessable Entity` responses.

## Service-Specific Error Handling

Third-party service integrations define their own exception classes within their respective adapter modules. For example, the RetroAchievements adapter in [`backend/adapters/services/rahasher.py`](https://github.com/rommapp/romm/blob/main/backend/adapters/services/rahasher.py) raises `RAHasherError`, while the IGDB integration uses `IGDBInvalidCredentialsException`. These encapsulate third-party API failures and credential issues separately from core RomM logic.

## Error Handling Flow and Integration

The RomM backend follows a consistent error propagation pattern across all layers:

1. **Internal utilities** raise domain-specific exceptions when encountering filesystem or data model issues.
2. **Socket endpoints** catch filesystem exceptions and emit WebSocket error events while logging via the central logger.
3. **API endpoints** allow endpoint exceptions to propagate, letting FastAPI automatically convert them to HTTP responses.
4. **Authentication** uses pre-built `HTTPException` objects for immediate client feedback.
5. **Background tasks** log scheduler exceptions and mark jobs as failed without HTTP interaction.
6. **Generic fallback** handlers catch `Exception` to log unexpected failures without masking specific error types.

This architecture ensures that every error is logged for observability while clients receive appropriate HTTP status codes and clear error messages.

## Summary

- RomM uses a **flat exception hierarchy** where all custom exceptions inherit directly from Python's built-in `Exception` class.
- **Filesystem exceptions** in [`backend/exceptions/fs_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/fs_exceptions.py) provide descriptive messages and are caught in socket endpoints to emit WebSocket notifications.
- **Endpoint exceptions** automatically raise `HTTPException` inside their constructors, causing FastAPI to return proper JSON error responses with correct status codes.
- **Authentication errors** are pre-instantiated `HTTPException` objects raised directly without custom class instantiation.
- **Background tasks** use `SchedulerException` for Redis-RQ job failures, logging errors without HTTP response generation.
- **Validation errors** are converted to `422 Unprocessable Entity` responses by FastAPI dependencies.

## Frequently Asked Questions

### How does RomM map custom exceptions to HTTP status codes?

RomM maps exceptions to HTTP status codes through the constructor pattern in [`backend/exceptions/endpoint_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/endpoint_exceptions.py). Each endpoint exception class raises `HTTPException` with a specific status code (such as `404` for not found or `500` for server errors) inside its `__init__` method. When the exception is raised anywhere in the codebase, FastAPI intercepts the embedded `HTTPException` and returns the corresponding JSON response to the client.

### What is the difference between filesystem and endpoint exceptions in RomM?

Filesystem exceptions in [`backend/exceptions/fs_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/fs_exceptions.py) are standard Python exceptions that carry descriptive messages for scan and folder structure errors. They are typically caught in socket endpoints and converted to WebSocket emissions or log entries. Endpoint exceptions in [`backend/exceptions/endpoint_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/endpoint_exceptions.py) are designed specifically for API routes and automatically raise FastAPI `HTTPException` objects to generate immediate HTTP responses without explicit try-catch blocks in the route handlers.

### How are authentication errors handled differently from other exceptions?

According to the RomM source code, authentication errors use pre-instantiated `HTTPException` objects rather than custom exception classes. Defined in [`backend/exceptions/auth_exceptions.py`](https://github.com/rommapp/romm/blob/main/backend/exceptions/auth_exceptions.py), objects like `AuthCredentialsException` are raised directly using `raise AuthCredentialsException`, eliminating the need for instantiation and ensuring consistent 401 Unauthorized responses across all authentication endpoints.

### Where are validation errors defined and how do they integrate with FastAPI?

Validation errors are defined in [`backend/utils/validation.py`](https://github.com/rommapp/romm/blob/main/backend/utils/validation.py) as the `ValidationError` class. These exceptions are raised by request-body validators when payload validation fails. FastAPI dependencies catch these exceptions and automatically convert them into `422 Unprocessable Entity` HTTP responses, providing detailed feedback about which fields failed validation constraints.