{"record":{"id":"2504fdedcb81fdcf","repo":"flowable/flowable-engine","slug":"lazy-loading-outside-command-context-for","errorCode":null,"errorMessage":"lazy loading outside command context for ","messagePattern":"lazy loading outside command context for ","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/ExecutionEntityImpl.java","lineNumber":961,"sourceCode":"        CommandContext commandContext = Context.getCommandContext();\n        if (commandContext == null) {\n            throw new FlowableException(\"lazy loading outside command context for \" + this);\n        }\n\n        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);\n        return processEngineConfiguration.getVariableServiceConfiguration().getVariableService()\n                .createInternalVariableInstanceQuery()\n                .executionId(id)\n                .withoutTaskId()\n                .name(variableName)\n                .singleResult();\n    }\n\n    @Override\n    protected List<VariableInstanceEntity> getSpecificVariables(Collection<String> variableNames) {\n        CommandContext commandContext = Context.getCommandContext();\n        if (commandContext == null) {\n            throw new FlowableException(\"lazy loading outside command context for \" + this);\n        }\n\n        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);\n        return processEngineConfiguration.getVariableServiceConfiguration().getVariableService()\n                .createInternalVariableInstanceQuery()\n                .executionId(id)\n                .withoutTaskId()\n                .names(variableNames)\n                .list();\n    }\n\n    // event subscription support //////////////////////////////////////////////\n\n    @Override\n    public List<EventSubscriptionEntity> getEventSubscriptions() {\n        ensureEventSubscriptionsInitialized();\n        return eventSubscriptions;\n    }","sourceCodeStart":943,"sourceCodeEnd":979,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/ExecutionEntityImpl.java#L943-L979","documentation":"ExecutionEntityImpl.getSpecificVariables lazy-loads a subset of an execution's variables on demand. Lazy loading only works inside a Flowable command context, because it needs the command context to reach the VariableService and run a query. When the entity is accessed outside a command (e.g. from a detached thread, a listener thread, or after the command finished), Context.getCommandContext() returns null and Flowable throws this FlowableException instead of returning partial data.","triggerScenarios":"Calling getVariables()/getVariable() on an ExecutionEntity (or TaskEntity, etc.) obtained from an async executor thread, a spring @Async method, a scheduled job, or a listener outside the command interceptor chain; holding a reference to an entity after CommandContext close and touching its variable collection later.","commonSituations":"Custom JobExecutors or thread pools using entities across threads; Spring integration where entity access happens outside a Flowable command (missing flowable spring command wrapper); unit tests constructing/inspecting entities directly without running a command.","solutions":["Wrap the entity access in a command: managementService.executeCommand(new CommandCommandContext...) or run the logic inside a Flowable command/listener so a CommandContext is active.","Load all needed variables while still inside the original command (e.g. in the JavaDelegate/TaskListener), pass values (not entities) to other threads.","For Spring apps, use the flowable-spring integration (SpringProcessEngineConfiguration) which keeps a command context open for the transaction, or annotate with @Transactional plus Flowable's command context propagation.","If async, re-fetch data via the public API (runtimeService.getVariables(executionId)) instead of touching the lazy entity."],"exampleFix":"// before (async thread, entity passed from a command)\nList<VariableInstanceEntity> vars = execution.getVariables();\n\n// after (inside the same command, or re-query via public API)\nMap<String, Object> vars = runtimeService.getVariables(execution.getId());","handlingStrategy":"validation","validationCode":"if (Context.getCommandContext() == null) {\n    // lazy loading unavailable: re-fetch via public API instead\n    Map<String, Object> vars =\n        runtimeService.getVariables(execution.getId());\n}","typeGuard":"boolean isInsideCommand() {\n    return Context.getCommandContext() != null;\n}","tryCatchPattern":"try {\n    vars = execution.getVariables();\n} catch (FlowableException e) {\n    if (e.getMessage().startsWith(\"lazy loading outside command context\")) {\n        vars = runtimeService.getVariables(execution.getId());\n    } else { throw e; }\n}","preventionTips":["Only touch entity lazy collections inside commands, delegates, or listeners.","Pass plain values (ids, copied maps) across thread boundaries, never entities.","In async code, re-query via runtimeService/taskService public API."],"tags":["flowable","lazy-loading","command-context","threading"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}