refactor: improve Dio error parsing and add user-friendly fallback messages for network and status code failures

This commit is contained in:
pradeepkumar
2026-04-09 05:22:11 +05:30
parent 509eae2e88
commit f9c6d5e5da

View File

@@ -231,16 +231,19 @@ class ApiClient {
/// Wrap a DioException with parsed error data.
DioException _wrapError(DioException error) {
final response = error.response;
// 1. Try to parse a structured error from the JSON response body
if (response != null) {
final data = response.data;
if (data is Map<String, dynamic>) {
final message = data['message'] is List
? (data['message'] as List).join(', ')
: data['message']?.toString() ?? 'An error occurred';
final rawMessage = data['message'] ?? data['error'];
final message = rawMessage is List
? (rawMessage).join(', ')
: rawMessage?.toString();
if (message != null && message.isNotEmpty) {
final errors = (data['errors'] as List<dynamic>?)
?.map(
(e) => FieldError.fromJson(e as Map<String, dynamic>))
?.map((e) => FieldError.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
@@ -255,16 +258,67 @@ class ApiClient {
);
}
}
}
// 2. Fallback — build a user-friendly message from the status code or
// Dio exception type. NEVER use `error.message` because Dio emits a
// giant technical blob ("This exception was thrown because the
// response has a status code of 401 and RequestOptions.validateStatus
// was configured to throw...") which is useless to end users.
return DioException(
requestOptions: error.requestOptions,
response: error.response,
error: ApiException(
message: error.message ?? 'Network error occurred',
message: _friendlyMessageFor(error),
statusCode: response?.statusCode,
),
);
}
String _friendlyMessageFor(DioException error) {
// Network-level failures take priority over status codes
switch (error.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
return 'The request timed out. Please check your connection and try again.';
case DioExceptionType.connectionError:
return 'Unable to reach the server. Please check your internet connection.';
case DioExceptionType.cancel:
return 'Request was cancelled.';
case DioExceptionType.badCertificate:
return 'Secure connection failed. Please try again later.';
case DioExceptionType.badResponse:
case DioExceptionType.unknown:
break;
}
// Status-code-based fallback
final code = error.response?.statusCode;
switch (code) {
case 400:
return 'The request was invalid. Please check your input.';
case 401:
return 'Invalid email or password.';
case 403:
return 'You don\'t have permission to perform this action.';
case 404:
return 'The requested resource was not found.';
case 409:
return 'This action conflicts with existing data.';
case 422:
return 'Some fields are invalid. Please check your input.';
case 429:
return 'Too many attempts. Please wait a moment and try again.';
case 500:
case 502:
case 503:
case 504:
return 'Server is temporarily unavailable. Please try again shortly.';
}
return 'Something went wrong. Please try again.';
}
}
class _QueuedRequest {