flowable/flowable-engine · error · FlowableException

lazy loading outside command context for " + this

Error message

lazy loading outside command context for " + this

What it means

ExecutionEntityImpl lazily loads variables on demand, but this requires an active command (transaction) context. getSpecificVariable throws this FlowableException when Context.getCommandContext() returns null, i.e. variable access happens outside any engine command, such as after the transaction/context closed.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/ExecutionEntityImpl.java:945

        // Record historic variable deletion
        CommandContextUtil.getHistoryManager().recordVariableRemoved(variableInstance);

        // Record historic detail
        CommandContextUtil.getHistoryManager().recordHistoricDetailVariableCreate(variableInstance, this, true,
            getRelatedActivityInstanceId(this), clock.getCurrentTime());
    }
    
    @Override
    protected boolean isPropagateToHistoricVariable() {
        return false;
    }

    @Override
    protected VariableInstanceEntity getSpecificVariable(String variableName) {

        CommandContext commandContext = Context.getCommandContext();
        if (commandContext == null) {
            throw new FlowableException("lazy loading outside command context for " + this);
        }

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        return processEngineConfiguration.getVariableServiceConfiguration().getVariableService()
                .createInternalVariableInstanceQuery()
                .executionId(id)
                .withoutTaskId()
                .name(variableName)
                .singleResult();
    }

    @Override
    protected List<VariableInstanceEntity> getSpecificVariables(Collection<String> variableNames) {
        CommandContext commandContext = Context.getCommandContext();
        if (commandContext == null) {
            throw new FlowableException("lazy loading outside command context for " + this);
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Read all needed variable values inside the command/command-context scope (e.g. within the delegate or a ProcessEngine-configured Command) and store plain values, not entities.
  2. Use an engine command to wrap access: managementService.executeCommand(cmd -> ...) and fetch variables there.
  3. Re-fetch variables through the runtime service API (runtimeService.getVariable(executionId, name)) instead of using the detached entity.
  4. If async access is needed, pass extracted variable values (copies) to the async code.

Example fix

// before (outside command context)
ExecutionEntity execution = ...; // captured earlier
String value = (String) execution.getVariable("key"); // lazy load -> throws
// after (inside command)
String value = managementService.executeCommand(commandContext ->
    CommandContextUtil.getExecutionEntityManager(commandContext)
        .findById(executionId).getVariable("key"));
Defensive patterns

Strategy: type-guard

Validate before calling

// Java: ensure command context exists before lazy variable access
import org.flowable.common.engine.impl.interceptor.CommandContextUtil;
boolean inCommandContext() {
    return Context.getCommandContext() != null;
}

Type guard

boolean canLazyLoad(ExecutionEntity e) {
    return Context.getCommandContext() != null && e.getId() != null;
}

Try / catch

try {
    Object v = execution.getVariable("key");
} catch (FlowableException e) {
    if (e.getMessage().startsWith("lazy loading outside command context")) {
        // re-fetch inside a command: managementService.executeCommand(...)
    } else { throw e; }
}

Prevention

When it happens

Trigger: Accessing variables on an ExecutionEntity outside the engine's command context — e.g. holding a reference to an execution entity in a delegate and reading variables after command completion, using entities in async threads, or touching detached/hydrated entities in application code after the engine API call returned.

Common situations: Caching ExecutionEntity instances across requests; spawning threads in delegates that read execution variables later; accessing lazy fields inside afterCommit callbacks or JTA Synchronization; returning entities from a service method and dereferencing variables in the view layer.

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/ce12e1f798278914. Report an issue: GitHub.