If all service-tasks are configured as asynchronous, each of them will be executed in their own job, each persisting the state to the database when the complete successfully. So the DB will contain a pointer to service task 3 after the failed execution of job. So the next time it executes, it will execute service task 3 again. There is no specific format for the exception to have in order to be picked up as a failed job. When an exception has no message, the EXCEPTION_MSG_ will be empty, but the reference to the stacktrace will be filled in.
Double-check if ALL service-tasks are marked as asynchronous. If this is the case, and you still get this behaviour, try ripping out all unneeded code and create a failing unit-test, demonstrating this problem. Then we can look at this asap… See the sticky in the forum for guide on creating a unit-test project.