Skip to content

Error Handling

Husnain Ali edited this page Sep 27, 2026 · 2 revisions

Error Handling

Every failure that crosses the public boundary is a NetworkError:

NetworkError
├── invalidURL(String)
├── noInternet
├── timeout
├── unauthorized(ResponseContext)
├── forbidden(ResponseContext)
├── notFound(ResponseContext)
├── validation(ResponseContext)
├── rateLimited(retryAfter: TimeInterval?, ResponseContext)
├── server(ResponseContext)
├── unacceptableStatusCode(Int, ResponseContext)
├── decoding(underlying: Error, ResponseContext?)
├── encoding(underlying: Error)
├── sslPinningFailed(host: String)
├── tokenRefreshFailed(underlying: Error)
├── sessionExpired
├── cancelled
├── offline
├── offlineQueued(RequestID)
├── transport(underlying: Error)
└── unknown(underlying: Error?)

Switch on the stable code discriminant, and read the response context when present:

do {
    let user = try await client.request(GetProfile())
} catch let error as NetworkError {
    switch error.code {
    case .unauthorized, .sessionExpired:
        await AppRouter.logout()
    case .notFound:
        showNotFound()
    case .validation:
        show(error.serverMessage ?? "Invalid input")   // decoded from the response body
    case .noInternet, .offline:
        showOfflineBanner()
    case .rateLimited:
        // error.responseHeaders?["Retry-After"]
        scheduleRetryLater()
    default:
        showGeneric(error)
    }
    // error.statusCode, error.responseHeaders, error.responseData also available
}

NetworkError.normalize(_:) maps an arbitrary thrown error into this type if you catch something loosely typed.

Important: NetworkError's errorDescription can include text taken from the server's response and from underlying errors, and is not passed through Redactor. Treat descriptions as untrusted when logging or reporting; use code and statusCode where a description could leak something sensitive. Credentials an OAuth token endpoint echoes back into an error body are masked, but that's the one place this is handled automatically.

Clone this wiki locally