Many of CDAP's services use Transactional or TransactionContext (from tephra) to execute code inside a transaction. If that code fails fails for any reason other than a transaction conflict, the exception is wrapped into a TransactionFailureException. This is because otherwise the execute() would have to declare to throw Exception (any exception that might come from the code being executed). However, that TransactionFailureException only carries the original exception, it has no significance by itself.
When logging or passing exceptions to error callbacks (such as onError()), we should unwrap the original exception from the TransactionFailureException first, and log or handle the original exception.
This would make it easier for user code to recognize the cause of failures and react appropriately.