M7 #16 (pass-2): don't bubble CancellationError into the chat banner

Pass-2 observed a spurious
"The operation couldn't be completed. (Swift.CancellationError error 1)"
banner appearing even after the resumed session loaded cleanly.

Root cause: when ChatController.startResuming tears down a prior live
session via `await stop()`, the in-flight event-task awaits throw
CancellationError as they unwind — that's how Swift concurrency
cooperatively cancels. That error then propagated through
recordACPFailure to the visible banner, even though nothing actually
failed.

Filter CancellationError (and the URL-loading equivalent,
NSURLErrorCancelled) out at the recordACPFailure boundary. Real
errors still flow through to the banner with hints + stderr details.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alan Wizemann
2026-04-24 14:44:18 +02:00
parent 3b3c037fce
commit d2633fb92d
@@ -120,7 +120,19 @@ public final class RichChatViewModel {
/// Populate the error triplet from a thrown Error + the ACPClient
/// we can query for recent stderr. Safe to call from anywhere
/// that catches an ACP op failure.
///
/// Swallows `CancellationError` silently it's how Swift's task
/// tree signals cooperative cleanup (e.g. when startResuming
/// tears down a prior live session via stop(), the event-task
/// awaits throw as they unwind). That's expected plumbing, not a
/// user-visible failure showing "The operation couldn't be
/// completed (Swift.CancellationError)" in the chat banner would
/// alarm users whose session actually loaded fine. Pass-2 UX fix.
public func recordACPFailure(_ error: Error, client: ACPClient?) async {
if error is CancellationError { return }
if (error as NSError).domain == NSURLErrorDomain, (error as NSError).code == NSURLErrorCancelled {
return
}
let msg = error.localizedDescription
let stderrTail = await client?.recentStderr ?? ""
let hint = ACPErrorHint.classify(errorMessage: msg, stderrTail: stderrTail)