flowable/flowable-engine · error · FlowableObjectNotFoundException

execution " + executionId + " doesn't exist

Error message

execution " + executionId + " doesn't exist

What it means

Flowable throws FlowableObjectNotFoundException when a command that requires an active execution cannot find the execution entity for the given executionId in the ACT_RU_EXECUTION table. The lookup happens in NeedsActiveExecutionCmd.execute before delegating to the concrete command, so any subclass command (e.g. signal, trigger, message event) on a stale or wrong execution id fails here.

Solutions

  1. Verify the executionId exists: query RuntimeService.createExecutionQuery().executionId(id).singleResult() before invoking the command.
  2. Re-fetch the execution id from a fresh process instance rather than reusing ids from a previous run or a completed instance.
  3. Confirm the app connects to the same database/schema where the process instance was started (check flowable datasource config).
  4. Catch FlowableObjectNotFoundException and surface a 404-style message to the caller instead of a 500.

Example fix

// before
runtimeService.trigger("unknown-exec-id");
// after
Execution execution = runtimeService.createExecutionQuery().executionId("unknown-exec-id").singleResult();
if (execution != null) {
    runtimeService.trigger(execution.getId());
}
Defensive patterns

Strategy: validation

Validate before calling

Execution exec = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
if (exec == null) { throw new NotFoundException("Execution " + executionId + " not found"); }

Type guard

boolean executionExists(String id) {
    return id != null && runtimeService.createExecutionQuery().executionId(id).count() > 0;
}

Try / catch

try {
    runtimeService.trigger(executionId);
} catch (FlowableObjectNotFoundException e) {
    log.warn("Execution {} no longer exists", executionId, e);
    throw new NotFoundException(e.getMessage());
}

Prevention

When it happens

Trigger: Calling a NeedsActiveExecutionCmd subclass (e.g. TriggerExecutionCmd, ExecutionSignalCmd) via RuntimeService/ExecutionService with an executionId that does not exist in the database.

Common situations: Using an execution id from a completed or deleted process instance; passing a processInstanceId instead of an execution id after restart; stale ids cached in client code across test runs; cluster with separate databases.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/NeedsActiveExecutionCmd.java:48

    private static final long serialVersionUID = 1L;

    protected String executionId;

    public NeedsActiveExecutionCmd(String executionId) {
        this.executionId = executionId;
    }

    @Override
    public T execute(CommandContext commandContext) {
        if (executionId == null) {
            throw new FlowableIllegalArgumentException("executionId is null");
        }

        ExecutionEntity execution = CommandContextUtil.getExecutionEntityManager(commandContext).findById(executionId);

        if (execution == null) {
            throw new FlowableObjectNotFoundException("execution " + executionId + " doesn't exist", Execution.class);
        }

        if (execution.isSuspended()) {
            throw new FlowableException(getSuspendedExceptionMessagePrefix() + " a suspended " + execution);
        }

        return execute(commandContext, execution);
    }

    /**
     * Subclasses should implement this method. The provided {@link ExecutionEntity} is guaranteed to be active (ie. not suspended).
     */
    protected abstract T execute(CommandContext commandContext, ExecutionEntity execution);

    /**
     * Subclasses can override this to provide a more detailed exception message that will be thrown when the execution is suspended.
     */
    protected String getSuspendedExceptionMessagePrefix() {

View on GitHub (pinned to d6d39ce1c6)