Making API Errors More Transparent

FEB 05

When learning how to use a new tool or service, the learning process usually goes something like this:

  1. Read the README, or some documentation.

  2. Write some code and try to run it.

  3. Run into an error.

  4. Determine what went wrong; go back to step 1.

There are a few keys to going through this process efficiently. One is to have a tight feedback loop between writing code and learning what went wrong; this is why developers put so much stake in ensuring that tests don't hit the disk or go over the network, things that slow down tests and hurt this loop.

Another key is ensuring that when you make a mistake, it's simple to determine what went wrong. What we were finding during our user testing was that it was difficult for new users to determine what went wrong when they used our client libraries. Consider a case where a user doesn't provide the correct Auth Token.

    from twilio.rest import TwilioRestClient
    token = ""
    client = TwilioRestClient("AC123", token)
    client.messages.create(...) # Etc, etc

When this request went to the API, it generated this fairly confusing stack trace:

    Traceback (most recent call last):
      File "tmp/errors.py", line 7, in <module>
        url="http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient")
      File "/Users/kevin/code/twilio-python/twilio/rest/resources/calls.py", line 115, in create
        return self.create_instance(kwargs)
      File "/Users/kevin/code/twilio-python/twilio/rest/resources/base.py", line 263, in create_instance
        data=transform_params(body))
      File "/Users/kevin/code/twilio-python/twilio/rest/resources/base.py", line 145, in request
        resp = make_twilio_request(method, uri, auth=self.auth, **kwargs)
      File "/Users/kevin/code/twilio-python/twilio/rest/resources/base.py", line 101, in make_twilio_request
        raise TwilioRestException(resp.status_code, resp.url, message, code)
    twilio.TwilioRestException: HTTP ERROR 401: Authenticate
     https://api.twilio.com/example

In testing we noticed the actual error - "HTTP ERROR 401: Authenticate" is hard for new users to parse, against the noise of the stack trace.

Part of the confusion - the one word error message - has an easy fix. Making it easier for people to parse a stack trace is a little trickier.

More Verbose Errors

We decided to try and print more information when a request comes up in an error. Specifically, we print out the method and URI you sent to Twilio, the error message, and a link to more information, all on separate lines. To make the error more visible, we use ANSI escape sequences to print out the errors in different colors.

Picture of pretty error
message

We use several helper functions to print out the escape sequences:

    def red(words):
        return "\033[31m\033[49m%s\033[0m" % words

It's important to note that different users have different default terminal background colors, so you'll need to set a consistent, bland background color as well.

One obvious worry is that we can't know how people are going to use the exception object that they receive from us. They might put it directly into a web UI, in which case the ANSI escape codes would appear as silly as they do above. To protect against this, we check if the standard error output is linked to a terminal, and only display the colorful error message if it would get printed to a terminal.

    import sys

    if hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
        return colorful_error_message()
    else:
        return "HTTP ERROR {status}: {message}".format(status=self.status,
            message=self.message)

We hope this new format is helpful to new users and experts alike. We are trialing it on the latest version of twilio-python, and welcome your feedback about its utility in helping you build your Twilio app.

Posted by Kevin Burke on February 05, 2014