A python microservice framework


Quick start
Project files
OpenAPI specification
API objects
Server code
Deployment pipeline
Docker packaging
JWT authentication
Error handling
Asynchronous execution
Database serialisation

Error Handling

Default Error model in PyMacaron APIs

If an endpoint raises an exception, it will be caught by pymacaron and returned to the caller in the form of an Error json object looking like:

    "error": "INVALID_USER",                      # Code identifying this error
    "error_description": "DB entry not found",    # Developer friendly explanation
    "user_message": "Sorry, we don't know you",   # User friendly explanation (optional)
    "status": 401                                 # Same as the response's HTTP status code

Custom errors

You can create your own errors by subclassing the class PyMacaronException.

To make the endpoint return a json Error object, just raise the exception:

from pymacaron.exceptions import PyMacaronException

class InvalidUserError(PyMacaronException):
    code = 'INVALID_USER'        # Sets the value of the 'error' field in the error json object
    status = 401                 # The HTTP reply status, and 'status' field of the error json object

# An endpoint implementation...
def do_login(userdata):
    raise InvalidUserError("Sorry, we don't know you")

Reporting non-fatal errors

Raising a PyMacaronException causes your endpoint to abort execution.

If you only want to report a non-fatal error but continue executing the endpoint, you may report the error by calling ‘report_error()’ from pymacaron.crash.

from pymacaron.exceptions import is_error
from pymacaron.crash import report_error

profile = ApiPool.user.client.get_profile()

# Did the 'get_profile' endpoint return an Error object?
if is_error(profile):
    # Send a crash report to your admins, including the error object
    report_error("Oops. Failed to get user profile", caught=profile)

The crash report above will have an auto-generated title starting with the text ‘NON-FATAL BACKEND ERROR’, to differentiate from crash reports that resulted from an exception in the server, reported as ‘FATAL BACKEND ERROR’.

You tell pymacaron what to do with crash reports by providing the ‘pymacaron.API’ constructor with an ‘error_reporter’ callback:

from pymacaron import API, letsgo

def my_crash_reporter(title, message):

    # title is a short description of the error, while message is a full json
    # crash dump in string format, containing a traceback of the exception
    # caught, data on the caller and runtime, etc. Now, send it to who you
    # want!

    send_email(to='', subject=title, body=message)
    tell_slack(channel='crashes', msg="%s\n%s" % (title, message))

def start(port=80, debug=False):

    api = API(

letsgo(__name__, callback=start)

Defining custom exceptions in batch

As an alternative to subclassing PyMacaronException, you can also define your own exceptions by calling the ‘add_error’ method as below:

from pymacaron.exceptions import add_error

# add_error() generates a class named MyOwnException that inherits from
# PyMacaronException and is properly handled by
# pymacaron. add_error() returns the MyOwnException class

exceptionclass = add_error(

# You can then inject the MyOwnException into the current module's namespace
globals()['MyOwnException'] = exceptionclass

# And now you can import it in other modules as well
# from myexceptions import MyOwnException

This is convenient if you want to define a large number of custom exceptions at once.

Unhandled errors

If your endpoint crashes, an Error instance with a status code above or equal to 500 will be sent back to the caller, and a crash report will be sent via ‘report_error’ and the ‘error_reporter’ callback.

Decorating errors with ‘error_decorator’

You can optionally intercept and manipulate all errors returned by a PyMacaron microservice by specifying an ‘error_decorator’ hook as follows:

from pymacaron import API, letsgo

def my_error_decorator(error):
    # Get errors in json format, and return the decorated error
    # In this example: we set a generic 'user_message' that is more
    # friendly that the error_description
    error['user_message'] = 'Something went really wrong! Try again later'
    return error

def start(port=80, debug=False):

    api = API(

letsgo(__name__, callback=start)