ChappieClientError covers everything that can go wrong during a request — missing credentials, usage caps, tool failures, transport errors, and unexpected server responses. ChappieAuthError covers failures during the sign-in flow itself. Both conform to LocalizedError, so error.localizedDescription always returns a human-readable string suitable for logging, even if you do not exhaustively switch on every case.
Catching ChappieClientError
AllChappieClient methods that communicate with the network are async throws. Wrap them in a do/catch block and match the cases you care about first, then fall through to a generic handler for anything unexpected.
ChappieClientError Cases
Every case inChappieClientError maps to a distinct failure mode. Expand a case to see what it means and how to respond.
missingCredential
missingCredential
ChappieSignIn or by calling authSession.startDeviceCodeSignIn().missingRefreshToken
missingRefreshToken
missingCredential in your UI.missingAccountID
missingAccountID
reauthenticationRequired(String)
reauthenticationRequired(String)
String value is a human-readable reason.Response: Show the reason string to the user if it is informative, then present the sign-in flow again.usageLimitReached(ChappieUsageLimit)
usageLimitReached(ChappieUsageLimit)
ChappieUsageLimit value carries plan, reset time, and window data — see ChappieUsageLimit fields below.Response: Show the user their plan name and when their quota resets. Avoid retrying immediately; the SDK’s built-in backoff already attempted retries before surfacing this error.missingToolHandler(String)
missingToolHandler(String)
ChappieHostTool was registered on the client. The associated String is the tool name.Response: Register a handler for every tool declared in your ChappieHarness. If the tool is optional, register a handler that returns a graceful “unavailable” result rather than omitting the registration entirely.toolApprovalDenied(String, String)
toolApprovalDenied(String, String)
String is the tool name; the second is the denial reason.Response: Log the denial for debugging. In most cases this is expected behaviour when a user declines a permission prompt; no recovery action is needed.toolExecutionFailed(String, String)
toolExecutionFailed(String, String)
String is the tool name; the second is the error message from the handler.Response: Investigate the handler implementation. You can surface a user-facing error message using the tool name, but avoid exposing raw internal error strings to users.toolLoopLimitExceeded(Int)
toolLoopLimitExceeded(Int)
Int is the limit that was exceeded.Response: Show a generic “could not complete” message. If this happens regularly, review whether your tool definitions create loops, or increase the loop limit in client configuration.responseTerminalFailure(ChappieResponse)
responseTerminalFailure(ChappieResponse)
ChappieResponse contains the raw status and any error payload.Response: Log the response for debugging. Show a generic retry prompt to the user; the failure is likely transient.streamingTransportReplayDiverged(String)
streamingTransportReplayDiverged(String)
String contains any partial text that was emitted before the divergence.Response: Show an error and give the user the option to retry. You can use the partial text string to reconstruct a best-effort display if needed.httpError(Int, String)
httpError(Int, String)
Int is the HTTP status code; the String is the response body.Response: Log the status and body. For 5xx errors, show a “service unavailable” message. For 4xx errors (other than 401 and 429, which the SDK handles automatically), check whether the request was well-formed.invalidResponse
invalidResponse
ChappieUsageLimit Fields
When you catchusageLimitReached, the associated ChappieUsageLimit value gives you structured data to build a meaningful error UI.
"usage_limit_reached".plan?.displayName for a user-facing plan name.Date.FormatStyle before showing it in your UI.usedPercent, windowMinutes, and resetsAt. Useful for building progress-bar or usage-meter UIs.Auth Errors (ChappieAuthError)
ChappieAuthError is thrown by auth-session methods such as startDeviceCodeSignIn() and refreshAuth(). Handle it wherever you initiate or observe the sign-in flow.
expiredDeviceCode
expiredDeviceCode
cancelledSignIn
cancelledSignIn
CancellationError was thrown during signing in.Response: Return to the signed-out state silently. No error UI is needed unless the cancellation was unexpected.networkFailure(String)
networkFailure(String)
URLError occurred during an auth network call. The associated String is the localized description from URLError.Response: Show a network-error message and offer a retry.pollingTimeout
pollingTimeout
callbackTimeout
callbackTimeout
missingRefreshToken
missingRefreshToken
tokenExchangeFailed(statusCode:body:)
tokenExchangeFailed(statusCode:body:)
statusCode is the HTTP status; body is the raw response body.Response: Log the status and body. For 400/401/403 responses, the requiresReauthentication property on the error is true — treat them as a sign-out and re-sign-in scenario.authorizationDenied(String)
authorizationDenied(String)
secureRandomFailed(statusCode:)
secureRandomFailed(statusCode:)
statusCode is the Int32 status returned by the system call.Response: Show a generic sign-in error. This is extremely rare; log the status code and file a bug if it recurs.invalidResponse
invalidResponse
unknown(String)
unknown(String)
String is the localized description of the underlying error.Response: Log the message for debugging. Show a generic sign-in error to the user.Rate Limiting and Automatic Backoff
Chappie automatically retries429 responses before surfacing usageLimitReached or httpError. The default policy attempts 2 retries with an initial delay of 1 second, a maximum delay of 30 seconds, and honours the Retry-After header when the server provides one.
To tighten or relax that behaviour, pass a custom ChappieRateLimitBackoffPolicy when creating the client:
429, use the .disabled preset — which is equivalent to ChappieRateLimitBackoffPolicy(maxRetries: 0):
ChappieRateLimitBackoffPolicy fields:
0 for .disabled behaviour.maximumDelay.true, the SDK uses the Retry-After value from the server response instead of computing its own delay. Set to false if you want purely client-side backoff timing.Handling Stream Cancellation
When you cancel a streaming task — by callinghandle.cancel() or cancelling a Swift Task — the stream throws either CancellationError or a URLError with code .cancelled. Both are normal and expected; catch them and take no action.
CancellationError or URLError.cancelled to users. They indicate the stream was stopped deliberately by your code, not an unexpected failure.