All Collections
Best Practices
Handling Error Callbacks
Handling Error Callbacks

How do I handle error responses from brainCoud?

Paul Winterhalder avatar
Written by Paul Winterhalder
Updated over a week ago


brainCloud is mostly un-opinionated when it comes to API callbacks.

However, your client app will generally always be making an authentication call first, before accessing the rest of the services on brainCloud.

SuccessCallback authSuccessCallback = (response, cbObject) =>
{
  // TODO handle the log in
};

_bc.AuthenticateAnonymous(authSuccessCallback, authFailureCallback);

If the authSuccessCallback is reached after authenticate, you can treat the user as successfully logged via your app, and take them to your main screen.

However, in the event an error occurs on authentication, your app and end-user will need to address the error before they can move onto any other client API calls.

To handle these errors, create a switch statement that compares against the reason code.

FailureCallback authFailureCallback = (status, code, error, cbObject) => {
  switch(code) {

  }
};

The CLIENT_NETWORK_ERROR_TIMEOUT (90001) error indicates that the brainCloud server could not be reached. The end user's device may have lost its internet connection. A suitable means of handling this event would be an error dialog informing the user to try again later.

... {
  switch(code) {
  case ReasonCodes.CLIENT_NETWORK_ERROR_TIMEOUT: {
    Debug.Log("Can't connect to the server. Try again later.");
    return;
    }
  }
};

The anonymous is the most straightforward login for brainCloud. If you are using email or universalId logins, you'll need to consider additional associated use cases.

MISSING_IDENTITY_ERROR (40206) indicates the account attempting to log in doesn't exist. The user may have requested their account to be deleted from your app's database. Handle the error by clearing the local credentials, and prompt the user to make a new account.

MISSING_PROFILE_ERROR (40208) indicates the user is attempting to login to an account that doesn't exist. If your app implements and login or register authentication flow, prompt the user to register to create their account.

EMAIL_NOT_VALIDATED (40214) indicates you have set your app to require email verification. Inform the user to check their email address for the verification letter, and after accepting, have them log in again. You can enable or disable email verification can on the Design | Authentication | Email Authentication page.

TOKEN_DOES_NOT_MATCH_USER (40307) indicates that the end-user is attempting to login to an account, and their password doesn't match the one initially provided. You can prompt the user to renter their password or to reset it with the ResetEmailPassword API call.

CLIENT_VERSION_NOT_SUPPORTED (40318) indicates that the end-user is using a version of your app that you no longer support. PLATFORM_NOT_SUPPORTED (40320) means they are using a platform you no longer support. Prompt your user to update to the latest version of your app, or which device they need to switch to keep using your app. You can set support versions and platforms on the brainCloud portal on the Design | Core App Info | Platforms page.

There are many potential error cases, and not all of them will apply to your app. A list of all the reason codes can be found in our API Reference.

For a non-login example, let's consider end-user using up some of their virtual currency to make a purchase.

_bc.VirtualCurrencyService.AwardCurrency("points", 1000, currencySuccessCallback, currencyFailureCallback);

In developing, you run into the error CURRENCY_SECURITY_ERROR (40491.) This error indicates the currency is currently set on the brainCloud portal, to disallow any client calls that manipulate currency. To fix, you have a development decision at hand. Either enable 'Allow Currency Calls from Client' on the Design | Core App Info | Advanced Settings page of the dashboard, or move your consume currency logic to cloud code.

Let's say within your app; you want to keep logic that handles currency in cloud code. So you write the following cloud code script to consume 1000 points.

var vcId = 'points';
var vcAmount = 1000;

var virtualCurrencyProxy = bridge.getVirtualCurrencyServiceProxy();
var postResult = virtualCurrencyProxy.consumeCurrency(vcId, vcAmount);

postResult;

And you call that script from your app.

_bc.ScriptService.RunScript("ConsumeCurrency", "", currencySuccessCallback, currencyFailureCallback);

Now when making an API call, there a general errors you will always want to check. Did your user lose their internet connection? Did they invalidate their session? Did you disable the app for maintenance?

It would take a lot of code duplication to check for these errors on every API call. It would instead be best to reuse part of the same callback function to handle these possible recurring errors.

void FailureCallback(int status, int code, string error, object cbObject) {

    generalErrors(status, code, error, cbObject);
}

public void generalErrors(int statusCode, int reasonCode, string statusMessage, object cbObject) {

  switch (reasonCode) {

    case ReasonCodes.NO_SESSION: {
      Debug.Log("Your session has expired. Please log in again.");
      return;
    }

    case ReasonCodes.USER_SESSION_LOGGED_OUT: {
      Debug.Log("You have logged in with another device. Please switch to the new device or log in again.");
      return;
    }

    case ReasonCodes.DISABLED_APP: {
      Debug.Log(
        "The app is currently disabled for maintenance. Please come back later.");
      return;
    }

    case ReasonCodes.CLIENT_NETWORK_ERROR_TIMEOUT: {
      Debug.Log("Can't connect to server. Try again later.");
      return;
    }
  }
}

Throughout your app, you can keep calling this new generalErrors function, to handle these non-unique errors, but we also want to handle an error specific to the ConsumeCurrency: INVALID_AMOUNT (40385).

Your initial assumption may be to handle the use case like so:

... {

  switch(code) {

  case ReasonCodes.INVALID_AMOUNT: {
    Debug.Log("You don't have enough points to use!");
    return;
    }
  }
};

However, you will quickly notice that this code will not work. The INVALID_AMOUNT error case will never be reached.

Why? brainCloud does not return response errors in your cc script as failures. Your cloud code script is custom to your app. So the system doesn't make assumptions if it passed or failed. If the cloud code script was able to be executed, then the server will always return the success callback.

That customization allows you to create whatever custom error codes you wish for your app. So to handle the INVALID_AMOUNT error, the code would have to be written as so.

void SuccessCallback(string response, object cbObject) {
  Dictionary<string, object> jsonMessage = (Dictionary<string, object>)BrainCloud.JsonFx.Json.JsonReader.Deserialize(response);

  Dictionary<string, object> jsonData = (Dictionary<string, object>)jsonMessage["data"];

  Dictionary<string, object> ccData = (Dictionary<string, object>)jsonData["response"];

  int status = (int)ccData["status"];

  if (status == 200) {
    Debug.Log("1000 currency consumed!");
  }
  else {
    int reasonCode = (int)ccData["reason_code"];
    switch (reasonCode) {
      case ReasonCodes.INVALID_AMOUNT: {
        Debug.Log("You don't have enough currency!");
        return;
      }
    }
  }
}

You can see in the above code; you will need to read the success response, and parse and handle any custom or brainCloud related error codes.

The final point is that instead of having a shared call back for general errors, you can also create a global callback with RegisterGlobalErrorCallback.

_bc.Client.RegisterGlobalErrorCallback(HandleGlobalErrorCallback);

public void HandleGlobalErrorCallback(int status, int reasonCode, string jsonError, object cbObject) {
  switch (reasonCode) {
    case ReasonCodes.UNABLE_TO_VALIDATE_PLAYER:
    case ReasonCodes.PLAYER_SESSION_EXPIRED:
    case ReasonCodes.NO_SESSION:
    case ReasonCodes.PLAYER_SESSION_LOGGED_OUT: {
      Debug.Log("YOUR SESSION HAS EXPIRED. RE-AUTHENTICATING...");
     
      // TODO display some form of re-authentication dialog.
     
      break;
    }
  }
}

This function callback will be triggered whenever an error occurs and can be used as a catch-all for general error cases.

Concluding thoughts

You should decide how you are going to handle the various error states in your app. General errors cases can be done in shared code to reduce duplication. Your cloud code errors cases are custom, and you can return any errors cases desired in the success response.

Happy coding!

Did this answer your question?