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

1 Like

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.

1 Like

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.