diff --git a/src/ReC.Client/ReCClientHelpers.cs b/src/ReC.Client/ReCClientHelpers.cs
index 0102710..44bc252 100644
--- a/src/ReC.Client/ReCClientHelpers.cs
+++ b/src/ReC.Client/ReCClientHelpers.cs
@@ -1,11 +1,10 @@
using System;
using System.Globalization;
using System.Linq;
-using System.Net.Http.Json;
-
-#if NETFRAMEWORK
using System.Net.Http;
-#endif
+using System.Net.Http.Json;
+using System.Threading;
+using System.Threading.Tasks;
namespace ReC.Client
{
@@ -45,5 +44,47 @@ namespace ReC.Client
/// The payload to serialize.
/// A instance ready for HTTP requests.
public static JsonContent ToJsonContent(T payload) => JsonContent.Create(payload);
+
+ ///
+ /// Throws a if the response indicates a non-success status code.
+ ///
+ /// The HTTP response to inspect.
+ /// A token to cancel the operation.
+ public static async Task EnsureSuccessAsync(HttpResponseMessage response, CancellationToken cancel = default)
+ {
+ if (response.IsSuccessStatusCode)
+ return;
+
+#if NETFRAMEWORK
+ string body = null;
+#else
+ string? body = null;
+#endif
+ if (response.Content != null)
+ {
+ try
+ {
+#if NETFRAMEWORK
+ body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+#else
+ body = await response.Content.ReadAsStringAsync(cancel).ConfigureAwait(false);
+#endif
+ }
+ catch
+ {
+ // Swallow body read failures; status info is still propagated.
+ }
+ }
+
+ var request = response.RequestMessage;
+ var method = request?.Method?.Method;
+ var uri = request?.RequestUri;
+
+ var message = $"ReC API request failed with status {(int)response.StatusCode} ({response.ReasonPhrase}). "
+ + $"{method} {uri}"
+ + (string.IsNullOrWhiteSpace(body) ? string.Empty : $": {body}");
+
+ throw new ReCApiException(message, response.StatusCode, response.ReasonPhrase, body, method, uri);
+ }
}
}