---
title: SAIF.NetCore.ErrorHandler architecture
summary: Architectural responsibilities, boundaries, and interaction patterns.
status: draft
owner: Framework.NCore maintainers
last_reviewed: 2026-10-02
library: SAIF.NetCore.ErrorHandler
reference_type: package
---

# SAIF.NetCore.ErrorHandler architecture

`SAIF.NetCore.ErrorHandler` is an implementation-layer library that centralizes exception translation and formatting for two hosting models: ASP.NET Core HTTP pipelines, through `ErrorHandler`/`IErrorHandler`, and Windows Service hosts, through `WindowsServiceErrorHandler`/`IWindowsServiceErrorHandler`; it codes only against `SAIF.NetCore.ServiceContracts` interfaces and relies on the host application to supply the logging and environment implementations and to wire it into the pipeline.

## Responsibilities
- Translate exceptions into HTTP responses: log the exception through `ISaifLogService`, capture the returned log ID as `ErrorID`, clear the response, add `Error-ID`/`Server-Name` headers, set the status code and content type, and write a handled or unhandled body (`ErrorHandler.LogAndReturnException`).
- Provide a legacy, `[Obsolete]`-marked conventional middleware entry point (`ErrorHandler.Invoke`) that classifies exceptions as `HttpStatusCodeException`, `SAIFException`, or any other `Exception`, in that precedence order.
- Build and log a formatted, multi-line Windows Service error (`WindowsServiceErrorHandler.HandleError`), raising `BeginErrorHandling` and `ErrorHandled` events so a host can veto logging, add context objects, or suppress special actions.
- Execute pluggable, fire-conditional special error actions (`SpecialErrorAction`/`SpecialErrorAction<T>`) that return a `SpecialErrorActionResult`; only the wrapped delegate call is contained in a try/catch — the fire-condition predicate, a non-conforming `ISpecialErrorAction` override, and the Error-level failure-logging call can all still throw uncaught, stopping the remaining actions and the subsequent `ErrorHandled` event from running (`ExceptionExtensions`).
- Flatten exception stack traces across the inner-exception chain and extract `SAIFException.LogDetails` as strings (`ExceptionExtensions`).

## Component boundaries
- `ErrorHandler` / `IErrorHandler`: ASP.NET Core-facing exception-to-response translator; implements the `IErrorHandleable` marker from ServiceContracts.
- `WindowsServiceErrorHandler` / `IWindowsServiceErrorHandler` (ServiceContracts): Windows Service-facing exception handler; owns `BeginErrorHandling`/`ErrorHandled` events, the `SpecialErrorActions` list, and the `ShutdownExemptExceptionTypes` list.
- `SpecialErrorAction` / `SpecialErrorAction<T>`: delegate wrappers with an optional fire-condition predicate, invoked by `WindowsServiceErrorHandler`.
- `ContextContainer` (`IErrorContextContainer`): string-keyed object bag passed to special actions during error handling.
- `WindowsServiceError` (`IWindowsServiceError`/`IStandardErrorLogInfo`): formats the identity, timestamp, exception, and log details into the logged error text.
- `ExceptionExtensions`: static helpers for stack-trace flattening and `SAIFException.LogDetails` extraction.
- This library has **no DI registration extensions, no middleware `Use*` extension, and no hosted service** of its own; every wiring point lives in `SAIF.NetCore.ServiceExtensions` and, for Windows Service hosts, `SAIF.NetCore.WindowsService`.

## Interaction model
- For HTTP hosts, `SAIF.NetCore.ServiceExtensions` registers `IErrorHandleable`/`ErrorHandler` and wraps the pipeline with its own `DynamicErrorHandler` middleware, which catches `HttpStatusCodeException`, `SAIFException`, or any other exception, classifies status/content-type/handled accordingly, and calls `IErrorHandler.LogAndReturnException` in this library to produce the response.
- For Windows Service hosts, `SAIF.NetCore.ServiceExtensions` registers `WindowsServiceErrorHandler` as a singleton `IWindowsServiceErrorHandler`, and `SAIF.NetCore.WindowsService`'s `ServiceWorker.HandleTaskError` first computes `IsExceptionExemptFromShutdown` and builds the error message, then calls `HandleError(message, ex)`, and only afterward conditionally stops the host using the previously computed exemption result.
- Both handlers resolve their logging dependency (`ISaifLogService`/`ISaifWindowsServiceLogService`) and `IEnvironmentClient` purely as ServiceContracts interfaces supplied at construction time; outbound calls to the SAIF Logging API happen inside `SAIF.NetCore.Logging`'s own implementation, not inside this library.

## Architecture diagram
The following diagram shows the two hosting paths this library serves and the ServiceContracts interfaces each depends on; all concrete implementations are supplied by the consuming host.

```mermaid
flowchart TD
    HTTP["ASP.NET Core host<br>(DynamicErrorHandler middleware,<br>SAIF.NetCore.ServiceExtensions)"] -->|IErrorHandler.LogAndReturnException| EH["ErrorHandler<br>(SAIF.NetCore.ErrorHandler)"]
    WS["Windows Service host<br>(ServiceWorker, SAIF.NetCore.WindowsService)"] -->|HandleError| WSEH["WindowsServiceErrorHandler<br>(SAIF.NetCore.ErrorHandler)"]
    EH -->|Error / Warning| LOG["ISaifLogService<br>(ServiceContracts contract)"]
    WSEH -->|Error| WSLOG["ISaifWindowsServiceLogService<br>(ServiceContracts contract)"]
    WSEH --> ACTIONS["SpecialErrorAction / SpecialErrorAction&lt;T&gt;"]
    WSEH -->|ApplicationName / ApplicationHost / TrackName| ENV["IEnvironmentClient<br>(ServiceContracts contract)"]
```

## Sequence diagram
The following diagram shows the Windows Service error-handling sequence, including the event hooks and special actions.

```mermaid
sequenceDiagram
    participant ServiceWorker
    participant WindowsServiceErrorHandler
    participant ISaifWindowsServiceLogService
    participant SpecialErrorAction
    ServiceWorker->>ServiceWorker: IsExceptionExemptFromShutdown? (computed first)
    ServiceWorker->>ServiceWorker: BuildErrorMessage(ex, exemptFromShutdown)
    ServiceWorker->>WindowsServiceErrorHandler: HandleError(message, ex)
    WindowsServiceErrorHandler->>WindowsServiceErrorHandler: Raise BeginErrorHandling
    alt not Handled by subscriber
        WindowsServiceErrorHandler->>ISaifWindowsServiceLogService: Error(ex, formattedText)
    end
    alt FireSpecialActions
        WindowsServiceErrorHandler->>SpecialErrorAction: Invoke(ex, context)
        SpecialErrorAction-->>WindowsServiceErrorHandler: SpecialErrorActionResult
    end
    WindowsServiceErrorHandler->>WindowsServiceErrorHandler: Raise ErrorHandled
    WindowsServiceErrorHandler-->>ServiceWorker: return
    ServiceWorker->>ServiceWorker: if not exemptFromShutdown, stop host (using value computed before HandleError)
```

## Design constraints
- No project references between libraries in this repository; the only cross-library dependency is the `SAIF.NetCore.ServiceContracts` NuGet package, consistent with the repository-wide layering in [`Architecture.md`](../../../Architecture.md).
- `BuildHandledResponseMessage` calls `contentType.ToLower()` without a null check; an `HttpStatusCodeException` with a `null` `ContentType` would throw after the response headers are already written, leaving the client with the status code and an empty body*.
- `WindowsServiceErrorHandler.HandleError` throws before logging when a `SAIFException`'s `LogDetails` is `null`, because `GetLogDetailsForSAIfException` calls `.Select` on `null`; the exception then escapes unlogged into `ServiceWorker.HandleTaskError`*.
- A local `IStandardErrorContent` interface duplicates the ServiceContracts `IStandardErrorLogInfo` contract minus `ID`, with no in-repository consumer found*.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [API reference](api/index.md)
- [SAIF.NetCore.ServiceContracts overview](../saif-netcore-servicecontracts/overview.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.

## Sources
- `src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/ErrorHandler.cs`
- `src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/ExceptionExtensions.cs`
- `src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/WindowsService/WindowsServiceErrorHandler.cs`
- `src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/WindowsService/SpecialErrorAction.cs`
- `src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/WindowsService/ContextContainer.cs`
- `src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/WindowsService/WindowsServiceError.cs`
- `src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IErrorHandleable.cs`, `ISaifLogService.cs`, `ISaifWindowsServiceLogService.cs`, `IEnvironmentClient.cs`, `IWindowsServiceErrorHandler.cs`
- `src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs`
- `Architecture.md`
