diff options
| author | zerafachris <christopher.zerafa@blocklabs.io> | 2026-07-17 16:44:25 +0200 |
|---|---|---|
| committer | zerafachris <christopher.zerafa@blocklabs.io> | 2026-07-17 16:44:25 +0200 |
| commit | d7727224c2f5024c9981bc70a274826beb7f1cdf (patch) | |
| tree | 185f8ce64658d678d2afbbe7a4429b7c29e5840a /Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs | |
| parent | a96dc8bd9b1e2fc2a897a7d73839abf5bc5b81d4 (diff) | |
Skip corrupt KeyframeData rows during full system backup
A single row with malformed KeyframeTicks JSON (e.g. a truncated array
from an interrupted write) currently aborts the entire backup, because
the try/catch in BackupService.CreateBackupAsync only wraps
serialization of an already-materialized entity, not the enumeration
itself. EF Core throws JsonReaderException from MoveNextAsync() while
materializing the corrupt row, which propagates past that catch block.
Switch to manual enumerator iteration so MoveNextAsync() failures can
be caught per-row, logged as a warning identifying the affected table,
and skipped, allowing the remaining rows and the rest of the backup to
complete.
Fixes #17216
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs')
| -rw-r--r-- | Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs | 44 |
1 files changed, 34 insertions, 10 deletions
diff --git a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs index a534fa5fa0..16daba0991 100644 --- a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs +++ b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs @@ -359,18 +359,42 @@ public class BackupService : IBackupService jsonSerializer.WriteStartArray(); var set = entityType.ValueFactory().ConfigureAwait(false); - await foreach (var item in set.ConfigureAwait(false)) + var enumerator = set.GetAsyncEnumerator(); + await using (enumerator) { - entities++; - try + while (true) { - using var document = JsonSerializer.SerializeToDocument(item, _serializerSettings); - document.WriteTo(jsonSerializer); - } - catch (Exception ex) - { - _logger.LogError(ex, "Could not load entity {Entity}", item); - throw; + // Reading the next row can itself throw, e.g. when a column contains malformed + // JSON (see https://github.com/jellyfin/jellyfin/issues/17216). Catch that here so a single + // corrupt row is skipped, logged for manual follow-up, and does not abort the whole backup. + bool hasNext; + try + { + hasNext = await enumerator.MoveNextAsync(); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Could not read next entity of type {Table}, the underlying data appears to be corrupt. Skipping this row and continuing backup; the affected database row should be inspected and fixed manually", entityType.SourceName); + continue; + } + + if (!hasNext) + { + break; + } + + var item = enumerator.Current; + entities++; + try + { + using var document = JsonSerializer.SerializeToDocument(item, _serializerSettings); + document.WriteTo(jsonSerializer); + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not load entity {Entity}", item); + throw; + } } } |
