rocksdanister/lively · error · UnauthorizedAccessException
Couldn't refresh tokens. You have to log in again
Error message
Couldn't refresh tokens. You have to log in again
What it means
Thrown by GalleryClient.RefreshTokensAsync when the token store has no AccessToken or no RefreshToken. Refreshing requires both; absence means there is nothing to refresh from, so the user must re-authenticate. It is an UnauthorizedAccessException so existing auth-error handlers pick it up.
Source
Thrown at src/Lively/Lively.Gallery.Client/GalleryClient.cs:156
public async Task<TokensModel> AuthenticateGithubAsync(string githubCode)
{
var message = new HttpRequestMessage(HttpMethod.Post, $"auth/google-token?code={githubCode}&provider=GITHUB");
var result = await SendAsync<TokensModel>(message, false);
var tokens = result.Data;
_tokenStore.Set(tokens.AccessToken, tokens.RefreshToken, "GITHUB", tokens.Expiration);
CurrentUser = await GetMeAsync();
if(CurrentUser != null)
{
LoggedIn?.Invoke(this, EventArgs.Empty);
}
return result.Data;
}
private async Task<TokensModel> RefreshTokensAsync()
{
if (Tokens?.AccessToken == null || Tokens?.RefreshToken == null)
throw new UnauthorizedAccessException("Couldn't refresh tokens. You have to log in again");
var message = new HttpRequestMessage(HttpMethod.Post, "auth/refresh")
.WithJsonContent(Tokens);
var result = await SendAsync<TokensModel>(message, false, true);
return result.Data;
}
public async Task<bool> LogoutAsync()
{
var message = new HttpRequestMessage(HttpMethod.Get, "auth/logout");
var result = await SendAsync<object?>(message);
CurrentUser = null;
_tokenStore.Set(null, null, null, DateTime.MinValue);
LoggedOut?.Invoke(this, EventArgs.Empty);
return result.Success;
}
#endregion
#region Gallery
public async Task DownloadWallpaperAsync(string id, string fileName, CancellationToken ct, Action<float, float, float> progressCallback = null)View on GitHub (pinned to c1036feb66)
Solutions
- Prompt the user to log in again via the gallery login flow, then retry the original operation.
- Before any authed call, ensure the token store has both an access and refresh token; if not, route to login.
- Investigate token persistence if tokens repeatedly vanish across restarts.
- Catch UnauthorizedAccessException around gallery calls and surface a 'session expired, please sign in' message.
Example fix
// before
if (Tokens?.AccessToken == null || Tokens?.RefreshToken == null)
throw new UnauthorizedAccessException("Couldn't refresh tokens. You have to log in again");
// after
if (Tokens?.AccessToken == null || Tokens?.RefreshToken == null)
{
LoggedOut?.Invoke(this, EventArgs.Empty);
throw new UnauthorizedAccessException("Session expired: please sign in again.");
} Defensive patterns
Strategy: validation
Validate before calling
if (string.IsNullOrEmpty(client.Tokens?.AccessToken) || string.IsNullOrEmpty(client.Tokens?.RefreshToken))
{
await NavigateToLogin();
return;
} Type guard
static bool HasRefreshableSession(GalleryClient c)
=> !string.IsNullOrEmpty(c.Tokens?.AccessToken)
&& !string.IsNullOrEmpty(c.Tokens?.RefreshToken); Try / catch
try { await client.SomeAuthedCall(); }
catch (UnauthorizedAccessException ex) when (ex.Message.Contains("refresh tokens"))
{ await NavigateToLogin(); } Prevention
- Check token-store presence before any authed operation.
- Verify token persistence works across restarts.
- Route all UnauthorizedAccessException from the gallery client through one login-handler.
When it happens
Trigger: RefreshTokensAsync() invoked when _tokenStore returns null/empty access or refresh token. Reached indirectly from InternalSendAsync when a request hits 401 and it tries to recover.
Common situations: User logged out but UI still issuing gallery calls; token persistence failed (file/credential-store write error) so a restart lost tokens; clock/token-store reset; first launch before any login.
Related errors
AI-assisted analysis of rocksdanister/lively@c1036feb66 (2026-08-13).
Data as JSON: /api/errors/df96030822d87619.
Report an issue: GitHub.