flowable/flowable-engine · error · ActivitiException

lazy loading outside command context

Error message

lazy loading outside command context

What it means

TaskEntity.getSpecificVariable lazy-loads a single task variable through the current CommandContext. Variables are persistence entities that can only be fetched inside an engine command. If the entity is accessed outside a command (no active CommandContext), the engine cannot load the variable and throws this ActivitiException.

Source

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

                    }
                }
            }
        }
    }

    @Override
    protected boolean isActivityIdUsedForDetails() {
        return false;
    }

    // Override from VariableScopeImpl

    // Overridden to avoid fetching *all* variables (as is the case in the super call)
    @Override
    protected VariableInstanceEntity getSpecificVariable(String variableName) {
        CommandContext commandContext = Context.getCommandContext();
        if (commandContext == null) {
            throw new ActivitiException("lazy loading outside command context");
        }
        VariableInstanceEntity variableInstance = commandContext
                .getVariableInstanceEntityManager()
                .findVariableInstanceByTaskAndName(id, variableName);

        return variableInstance;
    }

    @Override
    protected List<VariableInstanceEntity> getSpecificVariables(Collection<String> variableNames) {
        CommandContext commandContext = Context.getCommandContext();
        if (commandContext == null) {
            throw new ActivitiException("lazy loading outside command context");
        }
        return commandContext
                .getVariableInstanceEntityManager()
                .findVariableInstancesByTaskAndNames(id, variableNames);
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Read variables via TaskService.getVariable(taskId, name) inside your API call instead of on the entity directly
  2. Wrap custom code in managementService.executeCommand(new Command<...>() {...}) so a CommandContext exists
  3. Re-fetch a fresh task entity within the command rather than reusing a detached one
  4. For async work, pass taskId/values, not the entity

Example fix

// before
TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(id).singleResult();
executor.submit(() -> task.getVariable("x")); // throws outside command context
// after
executor.submit(() ->
    processEngine.getManagementService().executeCommand(ctx ->
        ctx.getTaskEntityManager().findById(id).getVariable("x"))
);
Defensive patterns

Strategy: type-guard

Validate before calling

boolean safe = org.activiti.engine.impl.context.Context.getCommandContext() != null;

Type guard

boolean inCommandContext() {
    return org.activiti.engine.impl.context.Context.getCommandContext() != null;
}

Prevention

When it happens

Trigger: Accessing task variables (getVariable/getVariableNames on a TaskEntity or its variable scope) from outside a command: e.g. in a non-managed thread, after the engine command finished, in application code holding a detached TaskEntity, or in custom code invoked without CommandContextInterceptor.

Common situations: Caching TaskEntity objects across requests and reading variables later; using entities inside async threads/executor tasks without wrapping in managementService.executeCommand; deserialized entities used in tests without a command context.

Related errors


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