Error Handling

When your web application throws an exception, whether user initiated or not, Alpas tries its best to handle it gracefully.

Based on the exception's status code, it first converts it to a proper HTTP exception type, such as NotFoundHttpException for 404, ValidationException for 422, MethodNotAllowedException for 405, InternalServerException for 500, etc. It then gives each exception a chance to report and then render the exception.

Each exception type does what is appropriate for it. Most of the exceptions return a message and a status code if the expected response is JSON.

For non-JSON responses, different exceptions do different things—ValidationException flashes errors and inputs making it available to the next request, both NotFoundHttpException and InternalServerException render a beautiful error page.

Default Error Templates

When you scaffold a new Alpas web app, two error templates for the most common HTTP exceptions — 404 Page Not Found and 500 Internal Server Exception — are created for you under resources/errors directory.

These error pages look great out of the box! However, you are more than welcome to modify them to make them fit more with your app's overall branding.

/alert/ Do not change the name or the location of the default error templates.

Handling Exceptions

Alpas's default way of handling the exceptions may or may not work for you. You may want to tweak it or completely change the behavior. Alpas allows you to easily intercept an exception being thrown and handle it the way you want it to. This applies for both the built-in exceptions and your own custom exceptions.

To intercept an exception, you need to provide your own subclass of dev.alpas.exceptions.ExceptionHandler class and then override one or more of report(), render(), or handle() methods.

  • report(exception: HttpException, call: HttpCall)

Override this method to log or send the given HTTP exception to external services like Bugsnag, Sentry etc. By default, the report() method passes the exception to the base class which then calls the report() method on the exception itself. Most of the built-in exceptions just logs a message either as a warning or as an error.

class ExceptionHandler : dev.alpas.exceptions.ExceptionHandler() {
    override fun report(exception: HttpException, call: HttpCall) {
        when (exception) {
            is NotFoundHttpException -> call.logger.warn { "This resource is missing!" }
            is ValidationException -> call.logger.warn { "The input is invalid!" }
            is MyCustomException -> ThirdPartyLogger.log(exception)
            else ->, call)

  • render(exception: HttpException, call: HttpCall)

Override this method to generate an HTTP response for a given exception and to send it back to the client. If you want to render a template for an exception, this is the place to do it. In fact, this is how the default 404 and 500 error pages are rendered by Alpas internally.

class ExceptionHandler : dev.alpas.exceptions.ExceptionHandler() {
    override fun render(exception: HttpException, call: HttpCall) {
        when (exception) {
            is MyCustomException -> call.render("errors/custom_error", 418)
            else -> super.render(exception, call)

  • handle(exception: Throwable, call: HttpCall)

Override this method if you want to handle non-http exceptions. You may be throwing a particular type of exception from different parts of your web app. This method is the perfect place to handle those types of exceptions in one central place. By default, this method converts any non-HTTP exception to a catch-all InternalServerException and reports and renders it accordingly.

/tip/ Your custom ExceptionHandler class can live anywhere in your package, but we recommend putting it under exceptions directory. Don't worry about loading this class. Alpas will automatically discover, load, and use this class for you!

Throwing HTTP Exceptions

Use HttpCall's one of many abort() methods to throw an HTTP exception.

Stacktrace Dump

When rendering an InternalServerException, if your app environment is set to dev mode, it dumps the stacktrace to your web page. This makes it easy to just see what's going on without having to resort to your IDE's console.

You can control whether the stacktrace gets dumped to your web page or not by setting a proper value for APP_LEVEL variable in your .env file. Setting it to development dumps the stacktrace; but, setting it to production doesn't.

/alert/ Make sure to set your app's level to production, before your app goes live to avoid exposing any internal data through a dumped stacktrace.