krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

b423f8a1cf3141d0b15ee899c1c5fe535ff982e9

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-04-13T22:41:34Z

fix: suppress false "network request failed" error on ticket list scroll

When scrolling through a large tracker, SwiftUI's .task modifier cancels
in-flight pagination requests as rows leave the screen. URLSession
responds with URLError(-999), which was being surfaced to the user as
"The network request failed. Please try again." — even though the
cancellation is expected and benign.

Guard against Task.isCancelled in the catch block so cancellations are
silently ignored; isLoadingMore is still reset unconditionally so the
next scroll attempt retries from the same cursor position.

Fixes: https://todo.sr.ht/~ccleberg/hutch/61
 Hutch.xcodeproj/project.pbxproj               | 12 ++++++------
 Hutch/Views/Tickets/TicketListViewModel.swift |  6 +++++-
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/Hutch.xcodeproj/project.pbxproj b/Hutch.xcodeproj/project.pbxproj
index 19792d4..d3d664e 100644
--- a/Hutch.xcodeproj/project.pbxproj
+++ b/Hutch.xcodeproj/project.pbxproj
@@ -515,7 +515,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 66;
+				CURRENT_PROJECT_VERSION = 67;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -532,7 +532,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0.5;
+				MARKETING_VERSION = 3.0.6;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -552,7 +552,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 66;
+				CURRENT_PROJECT_VERSION = 67;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -569,7 +569,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0.5;
+				MARKETING_VERSION = 3.0.6;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -642,7 +642,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 3.0.5;
+				MARKETING_VERSION = 3.0.6;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
@@ -671,7 +671,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 3.0.5;
+				MARKETING_VERSION = 3.0.6;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
diff --git a/Hutch/Views/Tickets/TicketListViewModel.swift b/Hutch/Views/Tickets/TicketListViewModel.swift
index 0255798..30e90a7 100644
--- a/Hutch/Views/Tickets/TicketListViewModel.swift
+++ b/Hutch/Views/Tickets/TicketListViewModel.swift
@@ -341,7 +341,11 @@ final class TicketListViewModel {
             hasMore = page.cursor != nil
             reconcileSelectionWithLoadedTickets()
         } catch {
-            self.error = error.userFacingMessage
+            // Ignore cancellation errors — the SwiftUI .task modifier cancels in-flight
+            // requests when a row scrolls off-screen, which is expected behavior.
+            if !Task.isCancelled {
+                self.error = error.userFacingMessage
+            }
         }
 
         isLoadingMore = false