Skip to content

Hive Axyl SDK error handling

How to check the cause and respond when a Hive Axyl SDK call fails. This page covers the order in which to branch on results, checking the HiveError that holds the cause of a Failure, the common Failure codes that differ by module, Add-on result handling, exceptions, and how to handle stored credentials in failure situations.

The properties and value lists of the types are in Core errors and Core result model. This page focuses on how to actually handle those values.

Method results

Methods that call the Hive Axyl server return a result object in one of the following four branches. For how to distinguish the result branches, see Core result model.

Branch When returned Handling
Success The server processed the request, and the response data was interpreted. Use the response data in Data.
Method-specific Outcome The server responded with a result code defined for this method. Provide user guidance or an alternative flow for each result.
UnknownOutcome This SDK version does not know the result code the server sent. See Unknown results.
Failure The call could not be completed, for example because of a network disconnection or call cancellation, or the server responded with an error that is not a feature-level result. Check the cause with the HiveError in Failure.Problem.

In a switch statement, branch in this order: Success, the Outcome results the app handles, and Failure. Because you must record Code and RawJson for UnknownOutcome, branch on it in a separate case and handle it as a failure, and handle the remaining Outcome results that the app does not handle separately in default. If you pass a cancellation token with ApiCallContext so that the call can be canceled, check whether the code is HiveErrorCode.Cancelled before you handle a Failure as a general error.

HiveError information

Check the cause of a Failure with the HiveError held in Failure.Problem. Use the following properties to distinguish the cause. For all properties and the values that Code can take, see Core errors.

  • Code: The HiveErrorCode value that classifies the failure cause
  • ExternalCode: The original error code sent by the server or platform. It holds a common Failure code sent by the server, a value that indicates the HTTP status such as HTTP 500, or a network error identifier such as Net_Offline.
  • Message: An English diagnostic message for identifying the cause
  • Type, Instance: The error type URI and the request path that the server sent in the error response

Even with the same Code, the cause and the response differ depending on ExternalCode, so check both values together, and record both when you log errors.

Handling by failure cause

Situation Code ExternalCode Handling
The device is not connected to the network. Unavailable Net_Offline The SDK does not retry this failure. Ask the user to check the network connection.
The SDK could not connect to the server, or the request timeout elapsed. Unavailable Net_ConnectionError Try again later. For idempotent requests, the SDK first retries automatically.
The request frequency exceeded the allowed limit. ResourceExhausted Net_RateLimited or a common Failure code sent by the server The SDK has finished its automatic retries, or it did not retry because the wait time the server required exceeded the maximum wait time. Wait a while and try again.
The call was canceled. Cancelled - Do not process the result. There is no guarantee that requests already delivered to the server are also canceled.
The server responded with a common Failure code. Determined by the HTTP status code. The common Failure code sent by the server See Common Failure codes by module.
The server responded with an error without an error code. Determined by the HTTP status code. A value that joins HTTP and the status code, such as HTTP 500 See HTTP status codes and HiveErrorCode.
The server's success response could not be interpreted. Internal - Record Code, Message, and the result's RawResponse.
An empty access token was specified as the credential. InvalidArgument - Check the token value you passed to ApiCallContext.WithAccessToken().

HTTP status codes and HiveErrorCode

If the server responds without an error code or with a common Failure code, Code is determined by the HTTP status code of the response as follows.

  • 400, 422: InvalidArgument
  • 401: Unauthenticated
  • 403: PermissionDenied
  • 404: NotFound
  • 409: AlreadyExists
  • 429: ResourceExhausted
  • 500: Internal
  • 501: Unimplemented
  • 502, 503: Unavailable
  • 504: DeadlineExceeded
  • Other 4xx: Unknown
  • Other status codes: Internal

Automatic retry

The SDK automatically resends requests that it judges to have failed transiently before it returns the result to the app. The Failure the app receives is the result after all automatic retries are finished.

Specify the default values for the retry count and timeout in Core configuration, and values that apply to a single call only in Core call context. Whether each failure type is retried automatically is as follows.

HTTP status code 429

When the SDK receives HTTP status code 429, it always retries. If the server sends a Retry-After header, the SDK waits that long, and if the wait time exceeds the configured maximum wait time, it fails with ResourceExhausted without waiting.

Server errors and server connection failures

When the SDK receives HTTP status code 500, 502, 503, or 504, or cannot connect to the server, it retries only idempotent requests. Idempotent requests are requests whose HTTP method is GET, PUT, DELETE, HEAD, or OPTIONS, and requests for which IsIdempotent is set to true per call.

Failures that are not retried

The SDK does not retry when the device is not connected to the network or when it receives a status code not described above. This includes 4xx status codes other than 429, and status codes such as 501 and 505.

Automatic token refresh failures

For methods called with the login session, if the server responds with 401 because the access token is no longer accepted, the SDK refreshes the token and resends the request. The app receives the result of the resent request. Requests called with a token that the app specified directly with ApiCallContext.WithAccessToken() are not subject to automatic refresh. Automatic refresh works only when refresh handling is registered with AuthTokenRefresh.Enable(), the automatic refresh setting is on, and the session has a refresh token.

If the refresh fails, the result differs as follows.

Refresh result Call result Handling
The server judged the refresh token invalid Unauthenticated The session is cleared, and OnSessionExpired is raised. Delete the saved credentials and ask the user to log in again.
No judgment was received because of a network or server error Unavailable The session is kept as is. Call again later.
A refresh in progress for another call was canceled Unavailable, with ExternalCode Auth_RefreshCancelled The session is kept as is. Retry the same call.
Refresh stopped after three consecutive refresh failures without a judgment Unavailable, with ExternalCode Auth_RefreshHalted Retrying does not recover from this. Check IsLoggedIn, and then log in again or restore the session with the saved credentials. Do not delete the saved credentials.
A request waiting for the refresh could not enter the queue because the limit was exceeded ResourceExhausted Retry the same call after the refresh finishes. Reducing the number of calls sent at the same time reduces this failure.

If the request resent after a successful refresh receives 401 again, the SDK does not repeat the refresh and returns an Unauthenticated Failure. When the refresh succeeds, OnSessionRefreshed is raised. The refresh token changes to a new value with every refresh, so update your saved credentials in this event.

Common Failure codes by module

Even when the server responds with an error code, common codes that are not feature-level results, such as those about the request format or authentication state, are returned as Failure, not Outcome. In this case, the code the server sent is held in Failure.Problem.ExternalCode, and Code is determined by the HTTP status code of the response. The list of common Failure codes differs by module, so check the list for the module you called.

Account and authentication

The methods of IAuthService and ITokenService return the following three codes as Failure. The code list is also summarized in Reference conventions.

  • invalid_parameter: A request parameter that does not match the format
  • missing_field: A required field or a required header itself is missing, such as when the X-App-Id header is not sent
  • missing_app_id: The X-App-Id header was sent, but its value is empty

Payment, mailbox, push notifications, coupons, and analytics logs

The methods of IPaymentsService, IMailboxService, IPushService, ICouponService, and IAnalyticsService return the following 14 codes as Failure. The per-module lists are also summarized in IPaymentsService, IMailboxService, IPushService, ICouponService, and IAnalyticsService.

  • bad_request: Invalid request
  • invalid_parameter: A request parameter that does not match the format
  • missing_field: A required field or a required header itself is missing, such as when the X-App-Id header is not sent
  • missing_app_id: The X-App-Id header was sent, but its value is empty
  • unauthorized: The authentication token is missing or invalid
  • token_expired: Authentication token expired
  • forbidden: No permission for the request
  • resource_not_found: Requested resource not found
  • method_not_allowed: Request method not allowed
  • resource_conflict: Conflict between the request and the resource state
  • unprocessable_content: Request content that cannot be processed
  • rate_limit_exceeded: Request frequency exceeded the allowed limit
  • internal_error: Internal server error
  • service_unavailable: Service temporarily unavailable

Codes defined for each method, such as payment_bad_request and resource_not_in_scope, are returned as Outcome, not as common Failure codes, even if their names are similar. Check the method-specific Outcome results in the response status of each method reference.

Service access control

The methods of IServiceAccessService return the following two codes as Failure. The code list is also summarized in IServiceAccessService.

  • invalid_parameter: A request parameter that does not match the format
  • missing_field: A required parameter or a required header itself is missing

In this module, internal errors that the server reports with the internal_error code branch to UnknownOutcome, not Failure, and the code is held in UnknownOutcome.Code.

TCB Connector

ITcbService does not declare common Failure codes. invalid_parameter, which other modules return as a common Failure code, is a result case in this module.

The methods of ITcbService return transport failures, call cancellations, and internal server errors that the server reported without an error code as Failure, and they return UnknownOutcome when the server responds with a code that is not among the result cases. For details, see Failure branches of ITcbService.

Unknown results

If this SDK version does not know the result code the server sent, UnknownOutcome is returned. This includes the case where a new result code was added to the server after the SDK was released, and the case where the server responded with a code not defined for the method. UnknownOutcome holds the following two properties.

  • Code: The result code the server sent
  • RawJson: The original text of the response body the server sent

Handle UnknownOutcome as a failure. Record Code and RawJson, and show the user a general failure message. Because the result has not been determined, do not delete stored credentials or the session based on this result.

Add-on results

Because Add-ons wrap store or operating system features, their results branch in situations different from those of server API modules. The results for each situation are as follows.

  • Unsupported platform: The Add-on is not registered, so HiveCore.Resolve<T>() throws an exception. Check first whether it is registered with HiveCore.TryResolve<T>().
  • A call before SDK initialization: A Failure whose Code is FailedPrecondition
  • The user cancels on an operating system screen: A method-specific Outcome such as UserCanceled
  • The app cancels with a cancellation token or a session cancellation method: A Failure whose Code is Cancelled
  • The store or operating system returns an error: A Failure that holds the error code the store sent in ExternalCode. For example, Google Play Billing errors are held in the BillingResponse:ITEM_ALREADY_OWNED format.

Add-on methods take a CancellationToken instead of ApiCallContext. Besides the payment Add-ons, the account and authentication Add-ons and the push notification Add-ons also branch their results in the same way. For the results and events of each method, find the Add-on reference in the module table of the Hive Axyl SDK reference.

Payment Add-ons

Payment Add-ons deliver some payment results as events rather than as method results, so you must handle both the method results and the events to keep the payment flow from breaking. How to check the results for each payment Add-on is as follows.

Apple StoreKit

The PurchaseAsync() results of the Apple StoreKit Add-on are divided into success, UserCanceled when the user closed the payment screen, Pending for the awaiting-approval state, UnknownOutcome, and Failure. The final result of a payment that received Pending is delivered later through the TransactionUpdated event, and you receive this event only while the transaction observer is started.

Even a success result can contain a transaction that did not pass signature verification, so verify the receipt with the Hive Axyl Server API on the app server. For the verification flow, see Receipt verification.

Google Play Billing

In the Google Play Billing Add-on, success of LaunchBillingFlowAsync() means that the payment screen was displayed. Check the actual purchase result in the BillingResult.ResponseCode and the purchase list of the PurchasesUpdated event. If the user cancels the payment, the event's response code indicates cancellation, and the purchase list is empty.

When the connection is lost, the BillingServiceDisconnected event is raised. The SDK does not reconnect, so restart the connection in the app.

Steam Microtransactions

The Steam Microtransactions Add-on delivers the payment authorization result through the MicroTxnAuthorizationResponse event. This event is raised only while the callback listener is receiving, so subscribe to the event first and then start the callback listener. If the event's Authorized is true, the user has authorized the payment; if it is false, the user has canceled it.

If the payment fails in the Steam overlay, no event is raised, so set a wait time for each order on the app server and handle it accordingly.

Rejection reasons in payment recipes

The recipes in the Payment practical guide call the Hive Axyl server and the market several times while processing a single payment. If one of those calls rejects the request, the Status of the recipe result becomes BusinessOutcome, and the rejection reason is delivered as a single PurchaseBusinessOutcome value regardless of which call it came from. Values may be added in minor releases, so include a default branch in your switch.

Value Meaning Handling
Unrecognized A result that the recipe has not named. See the Unrecognized description below.
PaymentBadRequest The server rejected the request format. Check the values you passed to the recipe. Calling again with the same values gives the same result.
PaymentInvalidParameter One of the request fields is invalid. Check the values you passed to the recipe. Calling again with the same values gives the same result.
PaymentResourceNotFound The order, product, or receipt that the call specified does not exist on the server. Check that the product ID and the order information are values from the same purchase.
PaymentResourceConflict The record to be saved already exists. This is the case where a previous run saved the same subscription. This is not a failure, so continue the flow according to the app server's receipt verification result.
PaymentUnauthorized The call was made without a usable login session. Check the login state, and then try again.
VerifyError The market rejected the receipt. The payment may already have been charged. Do not deliver the product. Keep the PendingPurchase, and then record the message the server sent.
StorePurchasePending The market has not received the payment yet. Google Play purchases waiting to be charged and Apple Ask to Buy purchases awaiting approval fall into this category. Do not deliver the product. When the payment is complete, the market delivers the purchase again.
StoreItemAlreadyOwned The market responded that the user already owns the product. For a consumable product, this means the previous purchase has not been consumed. Do not purchase again; finish the remaining purchase first.
NothingToRestore There is no unconsumed order to finish on the server. The user did not complete the payment, or the order has already been consumed. Retry the same call after the user completes the payment.

StorePurchasePending, StoreItemAlreadyOwned, and NothingToRestore are not codes sent by the server; they are responses from the market or judgments made by the recipe. The other values carry over the payment error codes sent by the server.

For Unrecognized, distinguish the two cases with UnknownOutcomeCode. If it has a value, this SDK version does not know the result code the server sent; if it is empty, the SDK knows the result, but the recipe does not have a value for it yet. In either case, record UnknownOutcomeCode and RawJson, handle it as a failure, and do not branch by guessing the value.

When you record a rejection, also record FailedStep, which tells you at which step the flow stopped. It is a diagnostic value, so do not branch app logic on it.

Exceptions

Predictable call results are returned as result objects. In contrast, problems that require fixing app code, such as when the SDK is not initialized, a required module is not registered, or a configuration value is invalid, are thrown as exceptions. Exceptions are not something to branch on and handle at runtime, so check the conditions under which they are thrown and fix the calling code or the configuration.

API Exception When thrown
HiveCore.Resolve<T>() InvalidOperationException It was called before the SDK was initialized.
HiveCore.Resolve<T>() RegistrationNotFoundException The requested service is not registered. The module was not registered when the SDK was initialized, or an Add-on was requested on an unsupported platform.
CoreConfigBuilder.Build() ArgumentException, ArgumentOutOfRangeException The App ID is empty, or a timeout or retry setting value is out of the allowed range.
Methods that take a request object ArgumentNullException null was passed as the request object.
Add-on methods ArgumentException The method was called with an argument that must have a value, such as a product ID list, left empty.

Call cancellation is returned as a Cancelled Failure, not as an exception. For exceptions thrown during SDK initialization, see Core module, and for the conditions for each method, see the 'Exceptions' table in each method reference.

Preserve stored credentials

The access token, the refresh token, and the guest account's guestToken saved on the device are the means by which users return to their accounts. A guest account cannot be recovered if this value is lost, so treat deletion as an irreversible operation. The default is to preserve them; delete them only when the situation-specific criteria below say to delete.

Delete credentials only when the user's explicit request is confirmed as successful or the server has determined that specific credentials are invalid. Deleting them on an undetermined error turns a temporary outage into permanent loss of the means to access the account. An invalidity judgment applies only to the credentials that were judged. For example, a judgment that the refresh token is invalid is not a reason to delete the guest account's guestToken.

Logout

Handle the stored credentials according to the logout result as follows.

  • Success: Delete
  • Network or server error: Preserve, and retry the logout
  • Call canceled: Preserve. However, if the result is success even though you canceled, the server has already processed the logout, so delete them.
  • Feature-specific result or UnknownOutcome: Preserve

If the logout ends with a network or server error, you cannot know whether the server processed the logout. If you delete the credentials at this point, you may lose the means to end the session that remains on the server.

Account deletion

Handle the stored credentials according to the account deletion result as follows.

  • Success: Delete
  • Feature-specific result or UnknownOutcome: Preserve
  • Network or server error: Preserve, and retry the request

Token refresh

Handle the stored credentials according to the cause of the automatic token refresh failure as follows. For how a refresh failure is delivered as a call result, see Automatic token refresh failures.

  • The server judged the refresh token invalid: Delete the token confirmed as invalid, and provide a flow to log in again
  • Failure without a judgment, or refresh halted: Preserve

Secure storage

Handle the stored credentials according to the result of operations on the secure storage that holds them as follows. For the meaning of each result, see Result handling of ISecureStorage.

  • Read failure or unknown result: Preserve. Do not clear the storage.
  • Access denied: Preserve. Do not clear the storage, and inform the user of the permission status.
  • Data corruption: Clear the storage, and provide a flow to log in again

Saving new credentials

If you cannot read the format of newly received credentials or a required value is missing, do not overwrite the existing credentials; preserve them.

Session restoration

When you restore the session with saved credentials, validate the access token first, and then register the session. If the Player ID in the validation result differs from the saved value, do not delete the saved values; ask the user to log in again. OnSessionExpired is not always raised when the session ends, so do not decide based on the event alone; also check IsLoggedIn.