Workflows + Exceptions (Java SDK)

I am almost embarrassed to even ask this question, but something doesn’t seem right.

Temporal 1.20.3, with Java SDK 1.19.1.

Imagine this super simple workflow:

    @Override
    public void execute() {
        throw new RuntimeException("whoops");
    }

When run, I’d expect this workflow to exit, and go into a failed state. That doesn’t happen. Instead, I get weird SDK errors re. replay, and the workflow stays in running state.

Either this is a bug, or I missed a memo somewhere. Any ideas?

Thanks!

Sean

This is by design. We don’t want workflows to fail on unexpected failures. It allows fixing them without losing workflows. Throw ApplicationFailure to fail the workflow.

So I just tried:

throw ApplicationFailure.newFailure(..)

. . . and it does the same thing – the workflow doesn’t go into a failed state.

Which gets me thinking. If an activity throws an exception and it’s not caught by the workflow, I’ll wager the workflow correctly goes into a failed state.

I should probably explain a little. Internally, the workflow is coded as a state-machine. If an error occurs, I am trying to figure out the best way to report the error (and/or an error-code). Querying the exception is probably a bad idea, because it means trawling the event-history. Probably saner to write the value(s) as search-attribute(s). In which case, we wouldn’t be throwing an exception, and it would just exit gracefully.

Sean

If you throw this failure from a workflow, it fails.

You probably threw this exception from an activity that by default, retries it. Either add this failure type to the list of RetryOptions.doNotRetry or throw ApplicationFailure.newNonRetryableFailure.

My code is from a workflow:

@WorkflowInterface
public interface ParentWorkflow {

    @WorkflowMethod(name = "ParentWorkflow")
    void execute();
}

public class ParentWorkflowImpl implements ParentWorkflow {

    @Override
    public void execute() {
        throw newFailure("whoops", null);
    }
}

This feels odd to me, this behaviour isn’t normal.

Sean

What is odd @sdonovan?

as Maxim stated, this behavior is by design and allows you to fix the code in case of exceptions like NPE without losing the workflow execution state.

@sdonovan I cannot reproduce the problem you reported. If an ApplicationFailure is thrown from the child workflow the parent receives it and fails if it is not handled.

Did you specify a child workflow retry policy by any chance?

Took me a minute – was just trying a few cases. Problem was mine – passing back the null in:

newFailure("whoops", null)

. . . . was causing the problem. I hadn’t noticed it.

Short version is:

  • From an activity, you can throw either a RuntimeException (gets wrapped as an ApplicationFailure), or an ApplicationFailure. Because it’s always returned as an ApplicationFailure, if not caught by the workflow, it’ll cause the workflow to fail.
  • However, from a workflow, you can only emit ApplicationFailure exceptions, which will cause the workflow to fail. Throwing RuntimeException from workflow logic is NOT supported, and won’t cause the workflow to fail.

I think? . . . and, thank you!

S

You can also configure your workflow to fail on any specific exception type (or on all if you configure Throwable as a type) by setting it in the WorkflowImplementationOptions.failWorkflowExceptionTypes.