Activiti/Activiti · error · ActivitiException

lazy loading outside command context

Error message

lazy loading outside command context

What it means

VariableScopeImpl.ensureVariableInstancesInitialized() lazy-loads all variable instances from the database, which requires an active command context. Outside a command (detached scope object, wrong thread) the load is impossible and this ActivitiException is thrown.

Solutions

  1. Access variables within the same command/delegate execution where the scope is live
  2. Wrap access in managementService.executeCommand(...) to establish a command context
  3. Copy needed variable values out during the command instead of keeping entity references

Example fix

// before
execution.getVariables(); // after command ended -> throws
// after
managementService.executeCommand(context ->
    context.getExecutionEntityManager().findById(executionId).getVariables());
Defensive patterns

Strategy: try-catch

Validate before calling

if (Context.getCommandContext() == null) {
    Map<String,Object> vars = runtimeService.getVariables(executionId);
}

Type guard

boolean scopeIsLive() {
    return Context.getCommandContext() != null;
}

Try / catch

try {
    vars = variableScope.getVariables();
} catch (ActivitiException e) {
    if (e.getMessage().contains("lazy loading")) {
        vars = runtimeService.getVariables(executionId);
    } else throw e;
}

Prevention

When it happens

Trigger: Accessing any variable API (getVariables, hasVariables, getVariable, etc.) on an ExecutionEntity/TaskEntity/VariableScope when Context.getCommandContext() is null — e.g. after the command finished, in another thread, or on a cached entity.

Common situations: Holding DelegateExecution/TaskEntity references after delegate completion; reading variables in async code without a command; using entities cached from a previous service call.

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 Activiti/Activiti@56435b1a97 (2026-09-09). Data as JSON: /api/errors/b1cfd98ce9027c0a. Report an issue: GitHub.

Appendix: source

Thrown at activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/VariableScopeImpl.java:65

    protected Map<String, VariableInstanceEntity> usedVariablesCache = new HashMap<String, VariableInstanceEntity>();

    protected Map<String, VariableInstance> transientVariabes;

    protected ELContext cachedElContext;

    protected abstract Collection<VariableInstanceEntity> loadVariableInstances();

    protected abstract VariableScopeImpl getParentVariableScope();

    protected abstract void initializeVariableInstanceBackPointer(VariableInstanceEntity variableInstance);

    protected void ensureVariableInstancesInitialized() {
        if (variableInstances == null) {
            variableInstances = new HashMap<String, VariableInstanceEntity>();

            CommandContext commandContext = Context.getCommandContext();
            if (commandContext == null) {
                throw new ActivitiException("lazy loading outside command context");
            }
            Collection<VariableInstanceEntity> variableInstancesList = loadVariableInstances();
            for (VariableInstanceEntity variableInstance : variableInstancesList) {
                variableInstances.put(variableInstance.getName(), variableInstance);
            }
        }
    }

    public Map<String, Object> getVariables() {
        return collectVariables(new HashMap<String, Object>());
    }

    public Map<String, VariableInstance> getVariableInstances() {
        return collectVariableInstances(new HashMap<String, VariableInstance>());
    }

    public Map<String, Object> getVariables(Collection<String> variableNames) {
        return getVariables(variableNames, true);

View on GitHub (pinned to 56435b1a97)