From 2326ecdedc042f7f1c2ce2a5da409377c09778d7 Mon Sep 17 00:00:00 2001 From: TowyTowy Date: Thu, 9 Jul 2026 13:39:09 +0200 Subject: Fix profile image being impossible to clear when its in-memory key is temporary ClearProfileImageAsync removed the ProfileImage instance attached to the passed-in User, but that instance can carry a stale, never-persisted (temporary) key because UpdateUserAsync creates the persisted image on a separately loaded entity and never copies the generated key back. Removing that detached entity on a fresh DbContext made EF Core throw InvalidOperationException ('ImageInfo.Id has a temporary value'), leaving the profile image impossible to delete or replace. Load the tracked, persisted user and remove its actual ProfileImage, matching the removal pattern already used in UpdateUserAsync. Adds regression tests covering the temporary-key case and the no-image no-op (the first fails before this change and passes after). Fixes #13137 Co-Authored-By: Claude Fable 5 --- Jellyfin.Server.Implementations/Users/UserManager.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 80722af106..b42fae751a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -882,8 +882,20 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - dbContext.Remove(user.ProfileImage); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + // Remove the tracked profile image loaded from the database instead of the + // detached instance on the passed in user. That instance can carry a stale, + // never-persisted (temporary) key, which makes EF Core throw when it is marked + // for deletion, leaving the profile image impossible to clear or replace. + var dbUser = await UserQuery(dbContext) + .AsTracking() + .FirstOrDefaultAsync(u => u.Id == user.Id) + .ConfigureAwait(false); + if (dbUser?.ProfileImage is not null) + { + dbContext.Remove(dbUser.ProfileImage); + dbUser.ProfileImage = null; + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } user.ProfileImage = null; -- cgit v1.2.3 From 47f567b6f1ad9729ad3721a5798cab03978300f5 Mon Sep 17 00:00:00 2001 From: Elian Van Cutsem Date: Sat, 11 Jul 2026 14:04:07 +0200 Subject: Keep authenticated user entity in sync with persisted login timestamps ExecuteUpdateAsync bypasses the EF change tracker, so the user entity returned by AuthenticateUser still carried the old LastLoginDate and LastActivityDate. SessionManager.LogSessionActivity then saved that stale entity in full, reverting LastLoginDate (usually to null) milliseconds after every login. Setting the properties on the entity keeps the follow-up save consistent and lets the 60-second activity guard skip the redundant write during login. Fixes #17301 --- Jellyfin.Server.Implementations/Users/UserManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 80722af106..a3e5f12382 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -616,6 +616,12 @@ namespace Jellyfin.Server.Implementations.Users .SetProperty(f => f.LastActivityDate, date) .SetProperty(f => f.LastLoginDate, date)) .ConfigureAwait(false); + + // ExecuteUpdateAsync bypasses the change tracker, so keep the + // returned entity in sync. Otherwise SessionManager.LogSessionActivity + // saves this (stale) entity in full and reverts LastLoginDate. + user.LastActivityDate = date; + user.LastLoginDate = date; } await dbContext.Users -- cgit v1.2.3