flowable/flowable-engine · error · PvmException

couldn't process signal

Error message

couldn't process signal '<signalName>' on activity '<activityId>': <cause>

What it means

PvmException thrown by ExecutionEntity.signal when the process virtual machine fails to deliver a signal to the current activity. Any non-runtime exception during signaling (waiting-state continuation, event triggering) is wrapped into this message carrying the signal name, activity id, and cause message. It indicates the wait state could not be continued — the underlying cause is the real problem.

Solutions

  1. Inspect the wrapped cause (e.getCause()) — the PvmException message only carries cause.getMessage(); fix the underlying exception first
  2. Verify the execution is actually waiting at the activity being signaled and that the signal/event name matches the wait state's subscription
  3. Check any ExecutionListener/delegate classes on the signaled activity for bugs (constructor access, missing classes, thrown exceptions)
  4. Log execution.getId() and activity id, and confirm the process state in the database before retrying

Example fix

// before
execution.signal("resume", null); // cause hidden
// after
try {
    execution.signal("resume", null);
} catch (PvmException e) {
    Throwable cause = e.getCause();
    log.error("Signal failed on activity: " + e.getMessage(), cause);
    throw cause instanceof RuntimeException ? (RuntimeException) cause : e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the execution is waiting where you expect before signaling
Execution e = runtimeService.createExecutionQuery()
    .executionId(executionId).activityId(expectedActivityId).singleResult();
if (e == null) throw new IllegalStateException("Execution not waiting at " + expectedActivityId);

Try / catch

try {
    execution.signal(signalName, signalData);
} catch (PvmException e) {
    Throwable cause = e.getCause();
    log.error("Signal '{}' on activity failed: {}", signalName, e.getMessage(), cause);
    throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause);
}

Prevention

When it happens

Trigger: Calling execution.signal(signalName, payload) or completing a wait state (receive task, user task via PVM internals, executionListener-triggered event) where the signaled activity's behavior throws a checked exception, e.g., a failing ExecutionListener, invalid event subscription, or a delegate throwing an Exception.

Common situations: Signaling an execution that has migrated to another activity, a delegate listener configured with a class that fails at runtime, sending a signal payload of the wrong type to a receive task, or triggering an event the activity is not actually waiting for.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/80e8e8f6ea1c42bc. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntity.java:467

            String signalledActivityId = activity.getId();
            activityBehavior.signal(this, signalName, signalData);

            // If needed, dispatch an event indicating an activity was signalled
            boolean isUserTask = (activityBehavior instanceof UserTaskActivityBehavior)
                    || ((activityBehavior instanceof MultiInstanceActivityBehavior)
                    && ((MultiInstanceActivityBehavior) activityBehavior).getInnerActivityBehavior() instanceof UserTaskActivityBehavior);

            if (!isUserTask && Context.getProcessEngineConfiguration() != null
                    && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
                Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createSignalEvent(
                        FlowableEngineEventType.ACTIVITY_SIGNALED, signalledActivityId, signalName, signalData, this.id, this.processInstanceId, this.processDefinitionId),
                        EngineConfigurationConstants.KEY_PROCESS_ENGINE_CONFIG);
            }

        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new PvmException("couldn't process signal '" + signalName + "' on activity '" + activity.getId() + "': " + e.getMessage(), e);
        }
    }

    @Override
    public void take(PvmTransition transition) {
        take(transition, true);
    }

    /**
     * @param fireActivityCompletionEvent This method can be called from other places (like {@link #takeAll(List, List)}), where the event is already fired. In that case, false is passed an no second event is fired.
     */
    @Override
    public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {

        if (fireActivityCompletionEvent) {
            fireActivityCompletedEvent();
        }

View on GitHub (pinned to d6d39ce1c6)