Exception Handling

Exceptions thrown during route execution bubble up through the route structure to the next enclosing handleExceptions directive, The runRoute Wrapper or the onFailure callback of a future created by detach.

Similarly to the way that Rejections are handled the handleExceptions directive delegates the actual job of converting a list of rejections to its argument, an ExceptionHandler, which is defined like this:

trait ExceptionHandler extends PartialFunction[Throwable, Route]

The runRoute Wrapper defined in The HttpService does the same but gets its ExceptionHandler instance implicitly.

Since an ExceptionHandler is a partial function it can choose, which exceptions it would like to handle and which not. Unhandled exceptions will simply continue to bubble up in the route structure. The top-most ExceptionHandler applied by The runRoute Wrapper will handle all exceptions that reach it.

So, if you’d like to customize the way certain exceptions are handled simply bring a custom ExceptionHandler into implicit scope of The runRoute Wrapper or pass it to an explicit handleExceptions directive that you have put somewhere into your route structure.

Here is an example:

import spray.util.LoggingContext
import spray.http.StatusCodes._
import spray.routing._

implicit def myExceptionHandler(implicit log: LoggingContext) =
  ExceptionHandler {
    case e: ArithmeticException =>
      requestUri { uri =>
        log.warning("Request to {} could not be handled normally", uri)
        complete(InternalServerError, "Bad numbers, bad result!!!")
      }
  }

class MyService extends HttpServiceActor {
  def receive = runRoute {
    `<my-route-definition>`
  }
}