From ad1170b42e70edc6d4bb3690c724ce1df1f3ea31 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:19:07 +0200 Subject: [PATCH] Fix duplicate threads causing a crash when exporting in parallel (#1486) Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../Discord/DiscordClient.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index 28d5e255..e076a97e 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -429,6 +429,11 @@ public class DiscordClient( .Where(c => before is null || c.MayHaveMessagesBefore(before.Value)) .ToArray(); + // Track yielded thread IDs to avoid duplicates that can occur when a thread transitions + // from active to archived between the two separate API calls used to fetch threads. + // https://github.com/Tyrrrz/DiscordChatExporter/issues/1433 + var seenThreadIds = new HashSet(); + // User accounts can only fetch threads using the search endpoint if (await ResolveTokenKindAsync(cancellationToken) == TokenKind.User) { @@ -472,7 +477,9 @@ public class DiscordClient( break; } - yield return thread; + if (seenThreadIds.Add(thread.Id)) + yield return thread; + currentOffset++; } @@ -511,7 +518,12 @@ public class DiscordClient( .Pipe(parentsById.GetValueOrDefault); if (filteredChannels.Contains(parent)) - yield return Channel.Parse(threadJson, parent); + { + var thread = Channel.Parse(threadJson, parent); + + if (seenThreadIds.Add(thread.Id)) + yield return thread; + } } } @@ -547,12 +559,14 @@ public class DiscordClient( ) { var thread = Channel.Parse(threadJson, channel); - yield return thread; currentBefore = threadJson .GetProperty("thread_metadata") .GetProperty("archive_timestamp") .GetString(); + + if (seenThreadIds.Add(thread.Id)) + yield return thread; } if (!response.Value.GetProperty("has_more").GetBoolean())