SignalMethod function not getting executed

I am facing an issue where @SignalMethod of my workflow is not getting executed.
Here is what my workflow looks like

@WorkflowInterface
public interface FeedWorkflow {

    @WorkflowMethod
    void execute(final WorkflowRequest request);

    @SignalMethod
    void processingComplete(final Summary summary);
}

public class FeedWorkflowImpl implements FeedWorkflow {

    private Summary summary = null;

    private final FeedWorkflowActivities activities;

    public FeedWorkflowImpl() {
        activities = Workflow.newActivityStub(FeedWorkflowActivities.class, <activityOptions>);
    }

    @Override
    public Summary execute(@NonNull final WorkflowRequest request) {
        try {
            activities.startProcessing(request);

            Workflow.await(() -> this.summary != null);

            return summary;
        } catch (final Throwable t) {
            logger.error("Error executing workflow: ", t);
            throw t;
        }
    }

    @Override
    public void processingComplete(@Nonnull final Summary summary) {
        logger.info("Received completion signal with '{}'", summary);
        this.summary = summary;
    }
}

and this is how i am signalling the workflow from a different module (not part of the workflow)

final FeedWorkflow workflow = 
                workflowClient.newWorkflowStub(FeedWorkflow.class, 
                                               workflowId, 
                                               Optional.of(runId));
workflow.processingComplete(summary);

The signalling completes successfully and in the UI i can see that the workflow has been signalled (WorkflowExecutionSignaled). But dont see the log statement inside my signal method (processingComplete method) being executed and also because of this the workflow is stuck in the await block

Any idea what i am missing?

I am also seeing the following error in the logs which i think i related

io.temporal.internal.sync.DestroyWorkflowThreadError: null at io.temporal.internal.sync.WorkflowThreadContext.lambda$destroy$597e554$1(WorkflowThreadContext.java:260) ~[pservice-1.0.jar:?] at io.temporal.internal.sync.WorkflowThreadContext.mayBeEvaluate(WorkflowThreadContext.java:103) ~[pservice-1.0.jar:?] at io.temporal.internal.sync.WorkflowThreadContext.yield(WorkflowThreadContext.java:80) ~[pservice-1.0.jar:?] at io.temporal.internal.sync.WorkflowThreadImpl.yield(WorkflowThreadImpl.java:402) ~[pservice-1.0.jar:?] at io.temporal.internal.sync.WorkflowThread.await(WorkflowThread.java:45) ~[pservice-1.0.jar:?] at io.temporal.internal.sync.SyncWorkflowContext.await(SyncWorkflowContext.java:775) ~[pservice-1.0.jar:?]

Never catch Throwable in the workflow code. The SDK uses Error subclasses to perform thread cleanup. Change your code to catch Exception instead.

1 Like

@maxim Thanks, that explains why i am seeing the error log, however as you can see i am re-throwing the throwable so i m guessing that is not root cause of the issue.

Also to give you some context the reason i was logging throwable in my code was because previously i was incorrectly trying to get a workflow stub(outside a workflow) by doing:
Workflow.newExternalWorkflowStub()
Since the above function when invoked outside a workflow throws an error instead of an exception, it took me a while to realize what the issue was. In order to get visibility into such errors while setting this up i decided to log them before re-throwing.

What version of Temporal are you using? I’m not able to reproduce this problem on my laptop.

our temporal server and client java library both are on 1.0.0

After a bunch of debugging i figured out the issue was because of deserialization of Summary object:

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Summary {
    private int totalItems;
    @Nullable
    private ErrorCode code;

    public Optional<ErrorCode> getErrorCode() {
        return Optional.ofNullable(code);
    }
}

This gets serialized fine and posted to the workflow as:

{totalItems:100, code:{value:null,empty:true,present:false}

But the deserialization back to the object fails. I was able to get it fixed by setting @JsonAutoDetect correctly

Anyways what made this difficult to debug was that there were no exceptions messages in the logs. Is there any way i can tap these internal exception/error messages into log4j?

Never mind, I think this is what i am looking for : https://github.com/uber/cadence-java-client/blob/master/src/main/java/com/uber/cadence/context/ContextPropagator.java

This is the log output I’m getting for unparsable signal:

08:25:47.652 [signal waitForName] ERROR i.t.i.sync.SyncWorkflowContext - Failure deserializing signal input for "waitForName" at eventId 5. Dropping it.
io.temporal.common.converter.DataConverterException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `io.temporal.samples.hello.HelloSignal$SignalArg` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"name":"World"}"; line: 1, column: 2]
	at io.temporal.common.converter.JacksonJsonPayloadConverter.fromData(JacksonJsonPayloadConverter.java:82)
	at io.temporal.common.converter.DefaultDataConverter.fromPayload(DefaultDataConverter.java:105)
	at io.temporal.common.converter.DataConverter.arrayFromPayloads(DataConverter.java:104)
	at io.temporal.internal.sync.SyncWorkflowContext.lambda$registerSignal$3ac69c8f$1(SyncWorkflowContext.java:675)
	at io.temporal.internal.sync.SyncWorkflowContext.signal(SyncWorkflowContext.java:639)
	at io.temporal.internal.sync.WorkflowExecuteRunnable.processSignal(WorkflowExecuteRunnable.java:71)
	at io.temporal.internal.sync.SyncWorkflow.lambda$handleSignal$2(SyncWorkflow.java:129)
	at io.temporal.internal.sync.CancellationScopeImpl.run(CancellationScopeImpl.java:104)
	at io.temporal.internal.sync.WorkflowThreadImpl$RunnableWrapper.run(WorkflowThreadImpl.java:107)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `io.temporal.samples.hello.HelloSignal$SignalArg` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"name":"World"}"; line: 1, column: 2]
	at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
	at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1455)
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1081)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1332)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4524)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3561)
	at io.temporal.common.converter.JacksonJsonPayloadConverter.fromData(JacksonJsonPayloadConverter.java:80)
	... 13 common frames omitted