From b3d5eab34fcfd5a1499c93fd9d78556e26f20465 Mon Sep 17 00:00:00 2001 From: siddseethepalli Date: Tue, 14 Apr 2026 01:57:45 +0000 Subject: [PATCH] fix(macos): guard SSE task against superseded session before calling bytes(for:) PR #25396 introduced a race on MainActor: a second call to startSSEStream() (which happens on boot via MainWindowView+Lifecycle and GatewayConnectionManager both calling startSSE) invalidates the session and cancels the first Task before its body has executed. When that cancelled Task later runs and calls session.bytes(for:), NSURLSession throws an NSGenericException from -[__NSURLSessionLocal taskForClassInfo:], which is uncatchable in Swift and crashes the process with SIGABRT. Guard the Task body with `Task.isCancelled` and an identity check on `self.sseSession === session` before touching the session, so a superseded Task exits cleanly instead of exploding inside URLSession. Co-Authored-By: Claude Opus 4.6 (1M context) --- clients/shared/Network/EventStreamClient.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clients/shared/Network/EventStreamClient.swift b/clients/shared/Network/EventStreamClient.swift index 875722f73b4..e42ead28e90 100644 --- a/clients/shared/Network/EventStreamClient.swift +++ b/clients/shared/Network/EventStreamClient.swift @@ -277,6 +277,15 @@ public final class EventStreamClient { sseTask = Task { @MainActor [weak self] in guard let self else { return } + // A back-to-back call to `startSSEStream()` on the MainActor can + // cancel this task and invalidate `session` before it runs its + // first instruction. Calling `session.bytes(for:)` on an already + // invalidated session throws an ObjC NSGenericException from + // `-[__NSURLSessionLocal taskForClassInfo:]`, which is + // uncatchable in Swift and crashes the process with SIGABRT. + // Bail out before touching the session if we've been superseded. + guard !Task.isCancelled, self.sseSession === session else { return } + do { let (bytes, response) = try await GatewayHTTPClient.stream( path: "assistants/{assistantId}/events",