[PR #2] [MERGED] fix(transport): treat POSIX socket-drop errors as transient so jobs recover via polling #7

Closed
opened 2026-10-26 22:33:50 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/comfy-swift-sdk/pull/2
Author: @mattmillerai
Created: 6/18/2026
Status: ✅ Merged
Merged: 6/18/2026
Merged by: @mattmillerai

Base: main ← Head: matt/be-1594-comfy-ios-backgrounding-a-running-job-shows-it-errored


📝 Commits (1)

  • 39c1eae fix(transport): treat POSIX socket-drop errors as transient so jobs recover via polling

📊 Changes

2 files changed (+128 additions, -0 deletions)

View changed files

📝 Sources/ComfySwiftSDK/Internal/Transport.swift (+16 -0)
➕ Tests/ComfySwiftSDKTests/TransportPOSIXTranslationTests.swift (+112 -0)

📄 Description

ELI5

When the app is backgrounded mid-job, the OS kills the underlying WebSocket connection. On resume, the dropped-socket error was arriving as an NSPOSIXErrorDomain code (e.g. ENOTCONN, ECONNRESET) which fell through Transport.translate to .unknown — a non-transient classification — so PollingFallback.isTransient returned false and the polling loop surfaced a false failure to the user. This change maps those OS-level socket-drop codes to .network(underlying:), which isTransient already recognises as transient, so the existing polling fallback takes over on resume and the job keeps going.

What

In Transport.translate(_:), added a POSIX branch between the existing URLError block and the final .unknown fallback. The branch checks ns.domain == NSPOSIXErrorDomain and maps ENOTCONN, ECONNRESET, ECONNABORTED, ENETDOWN, ENETUNREACH, ETIMEDOUT, EPIPE, and EHOSTUNREACH to .network(underlying: error).

Why

URLSession does not always wrap POSIX errors in URLError — it can surface them directly as NSError in NSPOSIXErrorDomain. The job itself is still running on the server; the error is purely a transport blip. Routing these codes through .network (already transient) is the correct classification: the polling fallback will back off, re-poll, and either resume from the last known state or deliver the completed output when the app comes back to the foreground.

No #if os(iOS) guards are needed; the POSIX constants (ENOTCONN etc.) are available on all Darwin platforms via Foundation.

Testing

  • swift build — clean, no warnings introduced.
  • swift test — 108 tests, 0 failures. Includes 12 new tests in TransportPOSIXTranslationTests.swift:
    • One test per POSIX code asserting PollingFallback.isTransient(result) == true.
    • Regression guards: URLError.cancelled still maps to .cancelled (non-transient), URLError.notConnectedToInternet still maps to .offline (transient), ComfyError.jobFailed still passes through non-transient, and an unrelated POSIX code (EBADF) does not become transient.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Comfy-Org/comfy-swift-sdk/pull/2 **Author:** [@mattmillerai](https://github.com/mattmillerai) **Created:** 6/18/2026 **Status:** ✅ Merged **Merged:** 6/18/2026 **Merged by:** [@mattmillerai](https://github.com/mattmillerai) **Base:** `main` ← **Head:** `matt/be-1594-comfy-ios-backgrounding-a-running-job-shows-it-errored` --- ### 📝 Commits (1) - [`39c1eae`](https://github.com/Comfy-Org/comfy-swift-sdk/commit/39c1eae04d176c729bcb5b553b3e006b4acb77dd) fix(transport): treat POSIX socket-drop errors as transient so jobs recover via polling ### 📊 Changes **2 files changed** (+128 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `Sources/ComfySwiftSDK/Internal/Transport.swift` (+16 -0) ➕ `Tests/ComfySwiftSDKTests/TransportPOSIXTranslationTests.swift` (+112 -0) </details> ### 📄 Description ## ELI5 When the app is backgrounded mid-job, the OS kills the underlying WebSocket connection. On resume, the dropped-socket error was arriving as an `NSPOSIXErrorDomain` code (e.g. `ENOTCONN`, `ECONNRESET`) which fell through `Transport.translate` to `.unknown` — a non-transient classification — so `PollingFallback.isTransient` returned `false` and the polling loop surfaced a false failure to the user. This change maps those OS-level socket-drop codes to `.network(underlying:)`, which `isTransient` already recognises as transient, so the existing polling fallback takes over on resume and the job keeps going. ## What In `Transport.translate(_:)`, added a POSIX branch between the existing `URLError` block and the final `.unknown` fallback. The branch checks `ns.domain == NSPOSIXErrorDomain` and maps `ENOTCONN`, `ECONNRESET`, `ECONNABORTED`, `ENETDOWN`, `ENETUNREACH`, `ETIMEDOUT`, `EPIPE`, and `EHOSTUNREACH` to `.network(underlying: error)`. ## Why `URLSession` does not always wrap POSIX errors in `URLError` — it can surface them directly as `NSError` in `NSPOSIXErrorDomain`. The job itself is still running on the server; the error is purely a transport blip. Routing these codes through `.network` (already transient) is the correct classification: the polling fallback will back off, re-poll, and either resume from the last known state or deliver the completed output when the app comes back to the foreground. No `#if os(iOS)` guards are needed; the POSIX constants (`ENOTCONN` etc.) are available on all Darwin platforms via `Foundation`. ## Testing - `swift build` — clean, no warnings introduced. - `swift test` — 108 tests, 0 failures. Includes 12 new tests in `TransportPOSIXTranslationTests.swift`: - One test per POSIX code asserting `PollingFallback.isTransient(result) == true`. - Regression guards: `URLError.cancelled` still maps to `.cancelled` (non-transient), `URLError.notConnectedToInternet` still maps to `.offline` (transient), `ComfyError.jobFailed` still passes through non-transient, and an unrelated POSIX code (`EBADF`) does not become transient. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-26 22:33:50 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/comfy-swift-sdk#7
No description provided.