Thrift Exception Mapping

It is recommended in Finatra that you generally prefer to use exceptions for flow control in your controller and services and rely on the c.t.finatra.thrift.exceptions.ExceptionMapper to convert exceptions into Thrift IDL defined exceptions or responses.


Exception mapping is meant to provide a server-wide mechanism for handling an exception in a standard manner.

The framework is not proscriptive about this, however. Sometimes it may make sense to catch and handle FooException directly in your controller (e.g., convert it to a Thrift response or perform another action). You are encouraged to do what makes sense for your use case or team.


The Finatra framework adds a default ExceptionMapper to your ThriftServer which provides a root-level no-op mapping for exceptions. You can register additional mappers or override the default altogether.

For instance, if you want to map a java.lang.ClassCastException to a ThriftException – e.g., ClientError(ClientErrorCause, errorMessage), which is defined in finatra_thrift_exceptions.thrift you could create the following ExceptionMapper:

import com.twitter.finatra.thrift.exceptions.ExceptionMapper
import com.twitter.finatra.thrift.thriftscala.{ClientError, ClientErrorCause}
import com.twitter.util.Future
import java.lang.ClassCastException
import javax.inject.Singleton

class ClassCastExceptionMapper
  extends ExceptionMapper[ClassCastException, ClientError] {

  def handleException(throwable: ClassCastException): Future[ClientError] = {
    Future.exception(ClientError(ClientErrorCause.BadRequest, throwable.getMessage))

Then register this exception mapper in your server.

import com.twitter.finatra.thrift.ThriftServer
import com.twitter.finatra.thrift.filters.ExceptionMappingFilter
import com.twitter.finatra.thrift.routing.ThriftRouter

class MyThriftServer extends ThriftServer {

  override def configureThrift(router: ThriftRouter): Unit = {

You can see here we register the exception mapper by type allowing the framework to instantiate an instance.

More examples of mapping exceptions to Thrift responses are located in the test mappers.


Servers written in Java should extend the AbstractThriftServer, which assumes you are using generated Java code and thus requires you to also use the JavaThriftRouter. See the section on defining servers for more information.


Using exception mappers requires you to include the c.t.finatra.thrift.filters.ExceptionMappingFilter in your server’s Filter chain.

For information on how to add a Filter to your ThriftServer see the Filters section.


The ExceptionMappingFilter works by attempting to match an incoming Exception type to a registered mapper for that type via the ExceptionManager. This means that you may need to pay attention to any generated exceptions or errors if you are generating code in multiple languages in your project. A generated Java Thrift Exception will be considered different from the same Exception generated in Scala.

Default Exception Mapper

The framework adds only the ThrowableExceptionMapper to the ExceptionManager by default which simply throws back any uncaught Throwable.

Throwable ThrowableExceptionMapper

This default ExceptionMapper is a no-op because the framework does not have a way to turn an exception into a meaningful Thrift IDL defined response. It is meant only to provide a backstop for unhandled exception types since the ExceptionManager walks the exception type hierarchy starting at the given exception type, moving up the inheritance chain until it finds a mapper configured for the type.

In this manner, an ExceptionMapper[Throwable] will be the last mapper invoked and acts as the “default”. Therefore to change the framework “default” mapper, simply add a new mapper over the Throwable type (i.e., ExceptionMapper[Throwable]) to the ExceptionManager.

Finatra Thrift Exceptions and Mapper

The Finatra framework provides both a FinatraThriftExceptionMapper and FinatraJavaThriftExceptionMapper for mapping exceptions to the finatra_thrift_exceptions.thrift defined exceptions.

For an example of including and using these exceptions see the test Thrift IDL defined here.

If you are using finatra_thrift_exceptions.thrift, then it is recommended that your register one of the above mappers appropriate to the generated language you are using.

Override Default Behavior

The ExceptionManager is the class that handles registration of exception mappers. In the example above, the ThriftRouter#exceptionMapper method is simply registering the given mapper with the ExceptionManager.

The ExceptionManager is configured by the inclusion of the ExceptionManagerModule as a framework module in every ThriftServer.

If a new mapper is added over an exception type already registered in the ExceptionManager, the previous mapper will be overwritten.

Thus, the last registered mapper for an exception type wins.

Register an Exception Mapper

There are several ways to add a mapper.

Either directly through the ThriftRouter:

override def configureThrift(router: ThriftRouter): Unit = {

With the JavaThriftRouter:

public void configureThrift(router: JavaThriftRouter) {

Or in a module which is then added to the Server, e.g.,

object MyExceptionMapperModule extends TwitterModule {
  override def singletonStartup(injector: Injector): Unit = {
    val manager = injector.instance[ExceptionManager]


override val modules = Seq(

in Java:

public class MyExceptionMapperModule extends TwitterModule {
  public void singletonStartup(Injector injector) {
    ExceptionManager manager = injector.instance(ExceptionManager.class);


public Collection<Module> javaModules() {
  return ImmutableList.<Module>of(new MyExceptionMapperModule());