Devolutions/UniGetUI · error · HttpRequestException
GitHub request failed with HTTP {(int)response.StatusCode} (
Error message
GitHub request failed with HTTP {(int)response.StatusCode} ({response.ReasonPhrase}): {GetErrorMessage(content)} What it means
GitHubApiClient.SendAsync is the single chokepoint for all GitHub REST/OAuth requests. After sending the HttpRequestMessage it reads the response body and checks IsSuccessStatusCode. On any non-2xx status it throws an HttpRequestException whose message includes the numeric status code, the ReasonPhrase, and a best-effort extraction of the error body (GetErrorMessage parses the JSON for message/error/error_description, falling back to the raw body). This surfaces rate limits, auth failures, 404s, and 5xx errors uniformly.
Source
Thrown at src/UniGetUI.Interface.IpcApi/GitHubApiClient.cs:233
.Select(value => new KeyValuePair<string, string>(value.Key, value.Value!))
),
};
message.Headers.Accept.Clear();
message.Headers.Accept.ParseAdd("application/json");
return await SendAsync(message, typeInfo, cancellationToken);
}
private async Task<T> SendAsync<T>(
HttpRequestMessage message,
JsonTypeInfo<T> typeInfo,
CancellationToken cancellationToken
)
{
using HttpResponseMessage response = await _httpClient.SendAsync(message, cancellationToken);
string content = await response.Content.ReadAsStringAsync(cancellationToken);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"GitHub request failed with HTTP {(int)response.StatusCode} ({response.ReasonPhrase}): {GetErrorMessage(content)}"
);
}
return JsonSerializer.Deserialize(content, typeInfo)
?? throw new InvalidOperationException("GitHub returned an empty response.");
}
private static StringContent CreateJsonContent<T>(T value, JsonTypeInfo<T> typeInfo)
{
return new StringContent(
JsonSerializer.Serialize(value, typeInfo),
System.Text.Encoding.UTF8,
"application/json"
);
}
private static string GetErrorMessage(string content)View on GitHub (pinned to 9b1d7d0eab)
Solutions
- Read the status code and body from the exception message to identify the specific GitHub error.
- If 401/403: re-authenticate via the device flow (CompleteGitHubDeviceFlowAsync) and retry.
- If 403 rate-limited: back off and retry after the Reset header interval; reduce request frequency.
- If 404 on a gist: the backup gist was deleted — call CreateCloudBackupAsync to recreate it.
- If 5xx: retry with exponential backoff or report a transient GitHub outage.
Example fix
// before: unguarded GitHub call
var user = await client.GetCurrentUserAsync();
// after: handle auth/rate-limit failures
try { var user = await client.GetCurrentUserAsync(); }
catch (HttpRequestException ex)
{
if (ex.Message.Contains("401") || ex.Message.Contains("403"))
await IpcBackupApi.CompleteGitHubDeviceFlowAsync();
throw;
} Defensive patterns
Strategy: try-catch
Try / catch
try { var user = await client.GetCurrentUserAsync(cancellationToken); }
catch (HttpRequestException ex)
{
Logger.Error($"GitHub API failure: {ex.Message}");
if (ex.Message.Contains("401") || ex.Message.Contains("403")) { /* re-authenticate */ }
if (ex.Message.Contains("403") && ex.Message.Contains("rate limit", StringComparison.OrdinalIgnoreCase)) { /* back off */ }
} Prevention
- Cache GitHub responses to reduce request volume and avoid rate limits.
- Use an authenticated client (higher rate limit) rather than anonymous where possible.
- Implement exponential backoff for transient 5xx responses.
When it happens
Trigger: Any GitHubApiClient call (GetCurrentUserAsync, GetCurrentUserGistsAsync, GetGistAsync, CreateGistAsync, EditGistAsync, device-flow token exchange) receives a non-success HTTP status. Examples: 401 when the stored token expired or was revoked; 403 with a rate-limit body; 404 when a gist ID no longer exists; 422 when a gist request payload is invalid; 5xx for GitHub outages.
Common situations: The GitHub OAuth token expired or was revoked (401). The client hit the unauthenticated rate limit of 60 req/hour (403). A gist was manually deleted between list and edit (404). The build's UNIGETUI_GITHUB_CLIENT_ID is wrong, producing a 404 or 401 on device-flow initiation. GitHub itself is down or degraded (5xx). Network proxy intercepts and returns a non-GitHub error body.
Related errors
- GitHub returned an empty response.
- The GitHub backup gist could not be created.
- The updater download server returned an invalid partial cont
- The updater download server returned partial content with a
- The pending GitHub device flow has expired. Start sign-in ag
AI-assisted analysis of Devolutions/UniGetUI@9b1d7d0eab (2026-08-13).
Data as JSON: /api/errors/19cdbae37c356e0a.
Report an issue: GitHub.