Expecting the Exceptions

Cyril Pillai
m.Paani
Published in
3 min readJul 25, 2018
Exceptions Everywhere

Most of the APIs have a defined format in which they return errors. This contract helps the API consumers in handling the errors.

At m.Paani, we too have a contract for raising errors. The API sends a JSON list containing N error objects. A typical error response would look something like

Format of a typical error response

field: Contains name of the field only if it’s a field error else returns non_field_errors

code: Internal Error Codes (as most non trivial systems have a lot of domain specific errors and edge cases). This also provides granularity and makes debugging a bit easier.

Consider this - API returns an HTTP 401 UNAUTHORIZED, it’s impossible to identify the actual cause. This could have been raised because token was invalid or expired or perhaps it wasn’t sent in the first place.

client_message: A user friendly message that can be directly shown to the user.

server_message: A message containing more technical information that can help in debugging the issue.

Now that we know the format of error we’re dealing with, let’s dive into handling it at the Android side.

Here’s the data class which would be used to deserialize the error body.

Class for deserializing JSON response

Here’s an example exception which would be raised once a corresponding JSON error is encountered.

Sample Exception Class

This is how you would be handling errors in your API calls.

Handling Errors
Wait! What’s going on here?

Let’s get to the interesting part, shall we?

Extension Functions for handling errors

handleErrors() is an extension function written in Kotlin that can be used on any Single.

It has a very straight forward responsibility

To transform the error response into a list of NetworkError and map every item to the corresponding Exception and return a CompositeException

RxJava’s onErrorResumeNext is the backbone of this extension function.

If you’ve never heard of this operator before, I would highly recommend reading a bit about it here, but just to summarize what it does

It replaces the current stream with a new Observable as soon as an error is encountered.

The function is pretty self explanatory but a higher level breakdown would be

  • Hijack the thrown exception.
  • Continue only if it’s an HttpException else just return a new Single.error() with the encountered exception to make sure the subscriber’s onError is called.
  • Now if it is an HttpException, then convert the response body into a List<NetworkError> using another extension function called parseError().
  • Iterate over the list of errors and add their corresponding exceptions to the errorList. This basically means handling all of the BASE errors like Not Found Exceptions, Auth Exceptions, Empty Field Exceptions, etc.
  • Add all of the unknown errors into the unhandledErrorList and pass it to the lambda function received. Most of the APIs would have unique errors which cannot/should not be handled by this function. It should rather be delegated to the caller.
  • Based on the function contract, the lambda function would then process the unhandledErrorList and return a List<Throwable> containing all of the API specific exceptions.
  • Add the new exceptions to the errorList and return a new Single.error() with the CompositeException containing all of the modified exceptions.
  • The subscriber can then iterate over the CompositeException and act accordingly.

What do you think about this approach. Feel free to share your thoughts.

If you liked what you just read and think you would be able to help us in solving challenging problems to empower the next billion users in India, then make sure you reach out to us at dream@mpaani.com. We are always looking for smart folks passionate about bringing a change in the society.

--

--

Cyril Pillai
m.Paani

👨🏾‍💻 Tech • 🍔 Food • 🏝 Travel • 😴 Sleep • Software Engineer @gojek