Context
PR #357 split three of CloudKit's 14 documented `serverErrorCode` values into dedicated `CloudKitError` cases:
- `.quotaExceeded(reason:, hint: QuotaHint?)` — 413 / `QUOTA_EXCEEDED`
- `.badRequest(reason:)` — 400 / `BAD_REQUEST`
- `.atomicFailure(reason:)` — 400 / `ATOMIC_ERROR`
Those three got promoted because they were either a) heavily overloaded at the wire level (`QUOTA_EXCEEDED` carries storage-quota, per-record size, and per-asset size) or b) semantically distinct from each other despite sharing an HTTP status (`BAD_REQUEST` vs `ATOMIC_ERROR`). The remaining 11 server codes still land in `.httpErrorWithDetails(statusCode:, serverErrorCode:, reason:)` with consumers needing string matching.
Proposed work
Enumerate the rest of the documented `serverErrorCode` values as dedicated cases so consumers can pattern-match by intent, and add an `.unknownServerError(code: String, statusCode: Int, reason: String?)` fallback so forward-compat is preserved when Apple adds new codes.
Codes to enumerate (from `openapi.yaml` `serverErrorCode` enum):
| Code |
HTTP |
Suggested case |
| `ACCESS_DENIED` |
403 |
`.accessDenied(reason:)` |
| `AUTHENTICATION_FAILED` |
401 |
`.authenticationFailed(reason:)` |
| `AUTHENTICATION_REQUIRED` |
421 |
`.authenticationRequired(reason:)` |
| `CONFLICT` |
409 |
`.conflict(reason:)` |
| `EXISTS` |
409 |
`.exists(reason:)` |
| `INTERNAL_ERROR` |
500 |
`.internalServerError(reason:)` |
| `NOT_FOUND` |
404 |
`.notFound(reason:)` |
| `THROTTLED` |
429 |
`.throttled(reason:)` |
| `TRY_AGAIN_LATER` |
503 |
`.tryAgainLater(reason:)` |
| `VALIDATING_REFERENCE_ERROR` |
412 |
`.validatingReferenceError(reason:)` |
| `ZONE_NOT_FOUND` |
404 |
`.zoneNotFound(reason:)` |
Plus `.unknownServerError(code: String, statusCode: Int, reason: String?)` for codes Apple may add in future spec revisions.
Why this is post-1.0 rather than blocking beta.2
This is a breaking API change that touches:
- `CloudKitError` enum (add 11 cases, change `httpErrorWithDetails` semantics)
- `ResponseProcessor` / `CloudKitError+OpenAPI.swift` (dispatch by code)
- Every consumer that pattern-matches `.httpErrorWithDetails(_, "
", _)`
- Every test that asserts on those patterns
PR #357 took the narrowly-scoped path: split only the three codes where there's either disambiguation value (`QUOTA_EXCEEDED` + `QuotaHint`) or semantic distinction (`ATOMIC_ERROR`). The remaining 11 don't have hint vocabularies and the action a consumer takes on them is roughly uniform (re-auth, back off, retry, surface) so the marginal value of splitting is lower.
Splitting them anyway gives type-safe matching and avoids "what's the current set of codes?" being a string-comparison concern. But it's API churn that doesn't need to land before beta.2.
Verification
- All existing `.httpErrorWithDetails(...)` pattern-match sites must be migrated to the new cases or to `.unknownServerError`.
- `MockTransport`-based tests should cover each code's roundtrip through the factory.
- The `.unknownServerError` path should be exercised with a mock returning a code not in the enum (forward-compat smoke test).
Related: closed sibling work in #356 (pre-1.0 hardening umbrella). Not adding this as a sub-issue of #356 since #356 is for pre-1.0 work; this is post-1.0.
🤖 Generated with Claude Code
Context
PR #357 split three of CloudKit's 14 documented `serverErrorCode` values into dedicated `CloudKitError` cases:
Those three got promoted because they were either a) heavily overloaded at the wire level (`QUOTA_EXCEEDED` carries storage-quota, per-record size, and per-asset size) or b) semantically distinct from each other despite sharing an HTTP status (`BAD_REQUEST` vs `ATOMIC_ERROR`). The remaining 11 server codes still land in `.httpErrorWithDetails(statusCode:, serverErrorCode:, reason:)` with consumers needing string matching.
Proposed work
Enumerate the rest of the documented `serverErrorCode` values as dedicated cases so consumers can pattern-match by intent, and add an `.unknownServerError(code: String, statusCode: Int, reason: String?)` fallback so forward-compat is preserved when Apple adds new codes.
Codes to enumerate (from `openapi.yaml` `serverErrorCode` enum):
Plus `.unknownServerError(code: String, statusCode: Int, reason: String?)` for codes Apple may add in future spec revisions.
Why this is post-1.0 rather than blocking beta.2
This is a breaking API change that touches:
", _)`PR #357 took the narrowly-scoped path: split only the three codes where there's either disambiguation value (`QUOTA_EXCEEDED` + `QuotaHint`) or semantic distinction (`ATOMIC_ERROR`). The remaining 11 don't have hint vocabularies and the action a consumer takes on them is roughly uniform (re-auth, back off, retry, surface) so the marginal value of splitting is lower.
Splitting them anyway gives type-safe matching and avoids "what's the current set of codes?" being a string-comparison concern. But it's API churn that doesn't need to land before beta.2.
Verification
Related: closed sibling work in #356 (pre-1.0 hardening umbrella). Not adding this as a sub-issue of #356 since #356 is for pre-1.0 work; this is post-1.0.
🤖 Generated with Claude Code