mirror of
https://github.com/Comfy-Org/comfy-swift-sdk.git
synced 2026-11-04 23:47:26 +00:00
[PR #62] [MERGED] fix: surface billing failures instead of burying them #65
Labels
No labels
enhancement
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-swift-sdk#65
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/comfy-swift-sdk/pull/62
Author: @mattmillerai
Created: 8/25/2026
Status: ✅ Merged
Merged: 8/25/2026
Merged by: @mattmillerai
Base:
main← Head:fix/surface-payment-and-execution-errors📝 Commits (1)
75d4c66fix: surface billing failures instead of burying them📊 Changes
5 files changed (+169 additions, -5 deletions)
View changed files
📝
Sources/ComfySwiftSDK/Internal/PollingFallback.swift(+24 -2)📝
Sources/ComfySwiftSDK/Internal/ReattachCoordinator.swift(+1 -2)📝
Sources/ComfySwiftSDK/Internal/Transport.swift(+21 -1)📝
Sources/ComfySwiftSDK/Public/ComfyError.swift(+6 -0)➕
Tests/ComfySwiftSDKTests/InsufficientCreditsTests.swift(+117 -0)📄 Description
ELI5
Someone ran a generation on an account with no credits. What Comfy Cloud actually said was:
What the app could show was a generic failure — so the user had no idea the problem was billing, and went looking for a bug instead.
The message was there the whole time. This SDK threw it away, in three different places.
The three paths
1.
checkStatushas no 402 branch. A 402 fell through todefault:and becameComfyError.network(URLError(.badServerResponse)). So a billing failure was reported as a connectivity failure — the app tells the user to check their internet connection when the real problem is an empty balance. That's not just vague, it's actively wrong advice.2.
checkBodyonly inspected 400 and 422. A 402's body was never parsed, so even when the server explained itself in plain English the message was discarded before anything could match on it.3. A failed job discarded
execution_errorentirely.PollingFallbackandReattachCoordinatorboth did:JobDetailResponse.executionError.exceptionMessage— the field holding the quote above — was never read. This is the path the real incident took: a partner node refused mid-run, and the consumer received nothing but "the job failed".The fix
Adds
ServerRejectionReason.insufficientCredits, and classifies to it from all three paths: 402 by status, a payment message by body, and a failed job by its execution error.It's deliberately distinct from
quotaExceeded, because the advice differs. A quota resets when the billing period rolls over — the user waits. Credits clear only by topping up — the user acts. Collapsing them would send half of these users to wait for something that is never going to happen.The credit match also runs before the quota match, for two reasons:
quota/limit exceeded/billing, so it previously fell all the way through to.otherand rendered as generic copy.billing, so quota-first would silently miscategorise it.Unrelated execution failures keep the existing
.jobFailed(phase:)shape — this only promotes failures it can positively identify.Testing
245 tests pass (6 new, in
InsufficientCreditsTests), covering each path: 402 status, payment message in the body, credit-wording-beats-quota, a partner node refusing mid-run, and the two negative cases — an unrelated execution error and a failure with noexecution_errorat all, both of which must still report.jobFailed.Breaking-change note
ServerRejectionReasonisn't frozen, but adding a case still breaks exhaustive switches over it in consumers. That's deliberate: the whole point is to make each consumer decide how to present a billing failure rather than let it fall into a generic bucket. In Comfy-iOS this surfaces as one compile error inErrorPresentation, which is the file that should be handling it.🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.