Devolutions/UniGetUI · warning · InvalidOperationException
The pending GitHub device flow has expired. Start sign-in ag
Error message
The pending GitHub device flow has expired. Start sign-in again.
What it means
CompleteGitHubDeviceFlowAsync retrieves the pending device flow (GetPendingGitHubDeviceFlow throws if none). It then compares DateTimeOffset.UtcNow against pending.ExpiresAtUtc, which was computed as UtcNow.AddSeconds(deviceFlow.ExpiresIn) at initiation. If the current time has passed the expiry, it clears the pending flow and throws InvalidOperationException. GitHub device codes typically expire after 15 minutes.
Source
Thrown at src/UniGetUI.Interface.IpcApi/IpcBackupApi.cs:211
{
Status = "success",
Command = "start-github-sign-in",
Message = request.LaunchBrowser
? "GitHub device flow started and the verification page was opened."
: "GitHub device flow started.",
Auth = await GetGitHubAuthInfoAsync(),
};
}
public static async Task<IpcGitHubAuthResult> CompleteGitHubDeviceFlowAsync()
{
EnsureGitHubClientConfigured();
PendingGitHubDeviceFlow pending = GetPendingGitHubDeviceFlow();
if (DateTimeOffset.UtcNow >= pending.ExpiresAtUtc)
{
ClearPendingGitHubDeviceFlow();
throw new InvalidOperationException(
"The pending GitHub device flow has expired. Start sign-in again."
);
}
try
{
using var client = CreateAnonymousGitHubClient();
var token = await client.CreateAccessTokenForDeviceFlowAsync(
Secrets.GetGitHubClientId(),
pending.DeviceFlow,
CancellationToken.None
);
if (string.IsNullOrWhiteSpace(token.AccessToken))
{
throw new InvalidOperationException("GitHub did not return an access token.");
}
View on GitHub (pinned to 9b1d7d0eab)
Solutions
- Call StartGitHubDeviceFlowAsync again to obtain a fresh device code, then complete it within the new ExpiresIn window.
- Poll CompleteGitHubDeviceFlowAsync at the interval returned by the device flow (PollIntervalSeconds) to avoid missing the window.
- Check the ExpiresAt timestamp from IpcGitHubAuthInfo and surface a countdown to the user so they act in time.
Example fix
// before: completing after the code expired
await IpcBackupApi.CompleteGitHubDeviceFlowAsync();
// after: restart the flow when expired
var auth = await IpcBackupApi.GetStatusAsync();
if (auth.Auth.DeviceFlowPending && auth.Auth.ExpiresAt > DateTimeOffset.UtcNow)
await IpcBackupApi.CompleteGitHubDeviceFlowAsync();
else
await IpcBackupApi.StartGitHubDeviceFlowAsync(new IpcGitHubDeviceFlowRequest { LaunchBrowser = true }); Defensive patterns
Strategy: validation
Validate before calling
var auth = await IpcBackupApi.GetStatusAsync();
if (auth.Auth.DeviceFlowPending && auth.Auth.ExpiresAt > DateTimeOffset.UtcNow)
await IpcBackupApi.CompleteGitHubDeviceFlowAsync();
else
await IpcBackupApi.StartGitHubDeviceFlowAsync(new IpcGitHubDeviceFlowRequest { LaunchBrowser = true }); Type guard
static bool DeviceFlowStillValid(IpcGitHubAuthInfo auth) =>
auth.DeviceFlowPending && auth.ExpiresAt is { } exp && DateTimeOffset.UtcNow < exp; Try / catch
try { await IpcBackupApi.CompleteGitHubDeviceFlowAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("expired"))
{ await IpcBackupApi.StartGitHubDeviceFlowAsync(new IpcGitHubDeviceFlowRequest { LaunchBrowser = true }); } Prevention
- Poll at PollIntervalSeconds to complete before the device code expires.
- Show the user a countdown based on ExpiresAt.
- Restart the flow immediately if expired rather than retrying the stale code.
When it happens
Trigger: StartGitHubDeviceFlowAsync was called to begin the flow, but the user took longer than the device-code lifetime (ExpiresIn seconds) to enter the code in the browser. By the time CompleteGitHubDeviceFlowAsync polls, DateTimeOffset.UtcNow >= pending.ExpiresAtUtc.
Common situations: The user walked away after the device-flow UI was shown and returned after 15+ minutes. The polling client had a long delay or retry loop that overshot the expiry. The system clock drifted significantly. The flow was started but never completed in the same session.
Related errors
- GitHub did not return an access token.
- GitHub sign-in did not complete successfully. Finish the dev
- No GitHub device flow is pending. Start sign-in first.
- GitHub sign-in is not configured for this build. UNIGETUI_GI
- GitHub authentication is required for cloud backups.
AI-assisted analysis of Devolutions/UniGetUI@9b1d7d0eab (2026-08-13).
Data as JSON: /api/errors/dff64d8f2c1d233c.
Report an issue: GitHub.