---
title: Map a SAIF exception to an HTTP status code
summary: Provide a static, catch-clause-based mapping from SAIF.API.Exceptions.* types to HttpStatusCode.
status: draft
owner: andmil
last_reviewed: 2026-09-15
library: SAIF.Web.API
reference_type: package
operation_id: OP-06
trigger: Called explicitly by a consumer (no call site exists inside this library).
---

# Map a SAIF exception to an HTTP status code

`ExceptionHandler.ToHttpError(Exception)` (`public static`) provides a reusable, deliberate catch-clause type dispatch from `SAIF.API.Exceptions.*` to `HttpStatusCode`.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-06 |
| **Trigger** | Called explicitly by a consumer (no call site exists inside this library). |
| **Inputs** | `Exception` |
| **Outputs** | `HttpStatusCode` |
| **Primary components** | `ExceptionHandler.ToHttpError(Exception)` |
| **Entry point** | `src/SAIF.Web.API/ExceptionHandler.cs` |

## Steps

1. `throw exception;` inside a `try` block — the method **deliberately re-throws to use catch-clause type dispatch** rather than `is`/pattern matching ([BR-WEBAPI-011](../business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)).
   - The idiom unwinds/rewinds the stack and overwrites the original stack trace on the passed instance.*
2. `catch (SAIF.API.Exceptions.InternalServerError)` → `500 InternalServerError` ([BR-WEBAPI-011](../business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)).
3. `catch (SAIF.API.Exceptions.NoMatchingDataException)` → `404 NotFound` ([BR-WEBAPI-011](../business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)).
4. `catch (SAIF.API.Exceptions.UnauthorizedAccessException)` → `403 Forbidden` ([BR-WEBAPI-011](../business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)).
   - SAIF's own type, not `System.UnauthorizedAccessException`.
5. `catch (SAIF.API.Exceptions.InvalidDataException)` → `400 BadRequest` ([BR-WEBAPI-011](../business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)).
6. Bare `catch` → `500 InternalServerError` ([BR-WEBAPI-011](../business-rules.md#br-webapi-011-saif-exception-types-map-to-fixed-http-status-codes)).
   - Default for every unmapped exception, including a `null` argument (`throw null` raises a `NullReferenceException` caught here).*

Catch-clause ordering is not significant here because the four concrete SAIF exception types are sibling types; no clause shadows another (`src/SAIF.Web.API.Core/SAIFException.cs`).

## Process flow

```mermaid
flowchart TD
	subgraph MainFlow["Catch-clause dispatch<br>path"]
		Start(["Consumer calls<br>ToHttpError"]) --> Try["throw exception inside<br>try"]
		Try --> Internal{InternalServerError?}
		Internal -->|No| NoData{NoMatchingDataException?}
		NoData -->|No| Unauth{"UnauthorizedAccess<br>Exception?"}
		Unauth -->|No| Invalid{InvalidDataException?}
	end
	Internal -->|Yes| S500a[500 InternalServerError]
	NoData -->|Yes| S404[404 NotFound]
	Unauth -->|Yes| S403[403 Forbidden]
	Invalid -->|Yes| S400[400 BadRequest]
	Invalid -->|No| S500b["500 InternalServerError<br>(bare catch)"]
```

## Failure and edge-case behavior
- A `null` exception argument raises `NullReferenceException` at the `throw`, which is caught by the bare `catch` and mapped to `500`.*

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `src/SAIF.Web.API/ExceptionHandler.cs`
- `src/SAIF.Web.API.Core/SAIFException.cs`
