Coding Liquids demonstrations show the error widget recovering while valid cached rows remain on screen.
Name the five API failure families
Offline sockets, timeouts, client responses, server responses, and malformed payloads require different user actions. Treat expected 4xx responses separately from retryable 5xx responses and keep parsing failures visible to developers.
A single catch-all message prevents the UI from offering the right recovery.
Translate exceptions beside the HTTP call
Catch SocketException, TimeoutException, and FormatException where the request and decode occur. Inspect the HTTP status before decoding a success model, mapping 401, 404, 429, and 500-series responses deliberately.
Catching Exception in the widget loses status, endpoint, and stack information before it can be classified.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
sealed class AppFailure implements Exception {
const AppFailure(this.message);
final String message;
}
final class OfflineFailure extends AppFailure { const OfflineFailure() : super('You are offline'); }
final class TimeoutFailure extends AppFailure { const TimeoutFailure() : super('The request timed out'); }
final class InvalidDataFailure extends AppFailure { const InvalidDataFailure() : super('The response was invalid'); }
final class HttpFailure extends AppFailure {
const HttpFailure(super.message, this.statusCode);
final int statusCode;
}
Future<Map<String, dynamic>> fetchProfile(http.Client client, Uri uri) async {
try {
final response = await client.get(uri).timeout(const Duration(seconds: 12));
if (response.statusCode < 200 || response.statusCode >= 300) {
throw HttpFailure('Request failed', response.statusCode);
}
return jsonDecode(response.body) as Map<String, dynamic>;
} on SocketException { throw const OfflineFailure(); }
on TimeoutException { throw const TimeoutFailure(); }
on FormatException { throw const InvalidDataFailure(); }
}
The Instagram error cards map transport, protocol, authentication, validation, and server failures to distinct interface states.
Return an AppFailure the UI can switch on
A sealed AppFailure hierarchy gives presentation code a finite set of offline, timeout, rejected, server, and invalidData cases. Attach safe context such as statusCode and retryAfter while keeping response bodies out of visible messages.
Throwing raw package exceptions couples every screen to the transport library.

Build production-ready Handle API Errors features
The Complete Flutter Guide turns handle api errors into maintainable app architecture, polished UI, and testable production code.
Enrol nowRetry only transient failures with backoff
A generic retry function can await an operation, test whether the failure is transient, and delay exponentially between attempts. Cap attempts and delay, honour Retry-After for rate limits, and never retry validation failures or an invalid password.
Unbounded retries drain batteries, multiply writes, and conceal a persistent outage.
Future<T> retry<T>(
Future<T> Function() operation, {
int maxAttempts = 3,
bool Function(Object error)? shouldRetry,
}) async {
for (var attempt = 1; ; attempt++) {
try {
return await operation();
} catch (error) {
final transient = shouldRetry?.call(error) ?? false;
if (!transient || attempt >= maxAttempts) rethrow;
await Future<void>.delayed(Duration(milliseconds: 300 * (1 << (attempt - 1))));
}
}
}
Pair each message with a next action
Use ScaffoldMessenger for a brief failed command and an inline error panel when the screen cannot continue. Wire Retry to the same operation with its original arguments, while Sign in again handles expired authentication.
A SnackBar that merely says something went wrong leaves the user stranded.
My API reliability post on LinkedIn looks at observability and privacy when a response cannot be trusted.

The Complete Flutter Guide: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowKeep stale content during refresh failures
When cached rows are still valid, retain them and show the refresh error above or below the list. Distinguish initialLoadError from refreshError so only the first can replace the entire body.
Discarding a populated list because a background refresh failed makes the app look less reliable than its data.
Widget refreshError(AppFailure failure) {
return MaterialBanner(
content: Text(switch (failure) {
OfflineFailure() => 'You are offline. Showing saved results.',
TimeoutFailure() => 'The refresh timed out.',
_ => 'Could not refresh the latest results.',
}),
actions: [TextButton(onPressed: refresh, child: const Text('Retry'))],
);
}
Record diagnostics without recording people
Log endpoint templates, status, attempt number, duration, exception type, and a request correlation identifier. Redact tokens, email addresses, free-form bodies, and query values before sending events to production logging.
A complete request dump can expose credentials or personal information while still omitting the timing clue needed to debug.
Design an error contract the UI can trust
Begin by separating failures the user can act on from failures that only engineering can investigate. Invalid form input can point to a field, an expired session can offer sign-in, and a lost connection can expose retry. A server defect should instead produce a calm generic message plus a correlation identifier for support. Keeping those categories in a small domain type stops widgets from parsing status codes or matching fragments of exception text.
Response bodies are not guaranteed to match the success schema. Some gateways return an HTML error page, a proxy can send an empty body, and an older API version may use different JSON keys. Check the status and content type before decoding, preserve a safe excerpt for diagnostics, and convert parsing failures into a defined protocol error. The original stack trace should remain available to logging or crash reporting even when the screen receives a friendly explanation.
Retries are appropriate only when the operation is safe to repeat. A GET normally qualifies; a payment or order submission may require an idempotency key supplied by the backend. Honour Retry-After for rate limits, cap exponential backoff, and let the caller cancel when the route disappears. Retrying every 4xx response wastes bandwidth and can lock an account faster. Authentication refresh should be serialised so ten failing requests do not trigger ten simultaneous refresh calls.
Test the mapping table directly with representative responses: malformed JSON, a validation payload, 401, 403, 404, 429, 500, timeout, DNS failure, and cancellation. Widget tests can then assert that each domain failure exposes the correct action and retains entered data. In production telemetry, record the endpoint template, method, status, duration, and correlation ID; omit access tokens, request bodies, and personal values. That evidence distinguishes a local connectivity spike from a failing server release without leaking the very data the error handler is meant to protect.
Common mistakes
- Name the five API failure families: In API failure handling, a single catch-all message prevents the UI from offering the right recovery; inspect this API failure handling cause before changing another API failure handling widget.
- Translate exceptions beside the HTTP call: In API failure handling, catching Exception in the widget loses status, endpoint, and stack information before it can be classified; inspect this API failure handling cause before changing another API failure handling widget.
- Return an AppFailure the UI can switch on: In API failure handling, throwing raw package exceptions couples every screen to the transport library; inspect this API failure handling cause before changing another API failure handling widget.
- Retry only transient failures with backoff: In API failure handling, unbounded retries drain batteries, multiply writes, and conceal a persistent outage; inspect this API failure handling cause before changing another API failure handling widget.
- Pair each message with a next action: In API failure handling, a SnackBar that merely says something went wrong leaves the user stranded; inspect this API failure handling cause before changing another API failure handling widget.
- Keep stale content during refresh failures: In API failure handling, discarding a populated list because a background refresh failed makes the app look less reliable than its data; inspect this API failure handling cause before changing another API failure handling widget.
Frequently asked questions
How does name the five API failure families work in API failure handling?
For API failure handling, the starting rule is that offline sockets, timeouts, client responses, server responses, and malformed payloads require different user actions. Apply this API failure handling rule first because name the five API failure families determines whether the API failure handling pattern fits.
Why does return an AppFailure the UI can switch on matter for API failure handling?
In API failure handling, attach safe context such as statusCode and retryAfter while keeping response bodies out of visible messages. Keeping return an AppFailure the UI can switch on at the API failure handling call site exposes the API failure handling return value directly.
What failure should I test first in API failure handling?
First reproduce the API failure handling case where a SnackBar that merely says something went wrong leaves the user stranded. Recovery is complete only when the displayed message offers a relevant next action and preserves the user’s work.
How can I verify API failure handling before release?
Exercise record diagnostics without recording people with real API failure handling inputs on every shipped platform. Inspect the final API failure handling callback or output; a successful API failure handling button tap alone is not proof.
Further reads
Connect API failure handling to the surrounding Flutter stack through these published tutorials:
- Flutter Development Guide 2026
- Flutter HTTP Requests: GET, POST, and JSON With the http Package
- Flutter Dio Tutorial: Interceptors, Timeouts, and Error Handling
- Flutter REST API Integration: Fetch, Display, and Refresh Data
- Flutter Pull to Refresh: RefreshIndicator Tutorial
Sources: Flutter framework and Dart API documentation; API failure handling examples verified against current stable Flutter.