flowable/flowable-engine · error · FlowableObjectNotFoundException

execution ${executionId} doesn't exist

Error message

execution ${executionId} doesn't exist

What it means

GetDataObjectsCmd loads the ExecutionEntity for the given id and throws FlowableObjectNotFoundException 'execution <id> doesn't exist' when the lookup returns null. The command can therefore validate arguments but not reference integrity: the execution must exist at call time.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/GetDataObjectsCmd.java:77

        this.executionId = executionId;
        this.dataObjectNames = dataObjectNames;
        this.isLocal = isLocal;
        this.locale = locale;
        this.withLocalizationFallback = withLocalizationFallback;
    }

    @Override
    public Map<String, DataObject> execute(CommandContext commandContext) {

        // Verify existence of execution
        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);
        }

        Map<String, VariableInstance> variables = null;

        if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, execution.getProcessDefinitionId())) {
            Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
            variables = compatibilityHandler.getExecutionVariableInstances(executionId, dataObjectNames, isLocal);

        } else {

            if (dataObjectNames == null || dataObjectNames.isEmpty()) {
                // Fetch all
                if (isLocal) {
                    variables = execution.getVariableInstancesLocal();
                } else {
                    variables = execution.getVariableInstances();
                }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Query execution existence before reading data objects and handle absence gracefully
  2. Stop persisting execution ids across process completion; use history service for finished instances
  3. Confirm the correct engine/datasource for the id
  4. Refresh cached ids frequently instead of storing them indefinitely

Example fix

// before
Map<String, DataObject> all = runtimeService.getDataObjects(oldExecutionId);
// after
if (runtimeService.createExecutionQuery().executionId(oldExecutionId).count() > 0) {
    Map<String, DataObject> all = runtimeService.getDataObjects(oldExecutionId);
} else {
    // fall back to historyService.createHistoricVariableInstanceQuery()
}
Defensive patterns

Strategy: try-catch

Validate before calling

long n = runtimeService.createExecutionQuery().executionId(executionId).count();
if (n == 0) return Collections.emptyMap();

Try / catch

try {
    Map<String, DataObject> all = runtimeService.getDataObjects(executionId);
} catch (FlowableObjectNotFoundException e) {
    // instance completed/purged; use historyService instead
}

Prevention

When it happens

Trigger: runtimeService.getDataObjects(executionId) with an id whose process instance already ended and was removed from runtime tables; ids from another Flowable deployment/database; history-only identifiers used against the runtime API.

Common situations: After a batch migration completes and old instances are deleted; cleanup jobs purging instances while consumers still cache ids; miswired multi-engine environments.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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