{"record":{"id":"264ca3ac1b69f7e4","repo":"flowable/flowable-engine","slug":"lazy-loading-outside-command-context-264ca3","errorCode":null,"errorMessage":"lazy loading outside command context","messagePattern":"lazy loading outside command context","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/VariableScopeImpl.java","lineNumber":68,"sourceCode":"    protected Map<String, VariableInstance> transientVariabes;\n\n    protected ELContext cachedElContext;\n\n    protected String id;\n\n    protected abstract List<VariableInstanceEntity> loadVariableInstances();\n\n    protected abstract VariableScopeImpl getParentVariableScope();\n\n    protected abstract void initializeVariableInstanceBackPointer(VariableInstanceEntity variableInstance);\n\n    protected void ensureVariableInstancesInitialized() {\n        if (variableInstances == null) {\n            variableInstances = new HashMap<>();\n\n            CommandContext commandContext = Context.getCommandContext();\n            if (commandContext == null) {\n                throw new ActivitiException(\"lazy loading outside command context\");\n            }\n            List<VariableInstanceEntity> variableInstancesList = loadVariableInstances();\n            for (VariableInstanceEntity variableInstance : variableInstancesList) {\n                variableInstances.put(variableInstance.getName(), variableInstance);\n            }\n        }\n    }\n\n    @Override\n    public Map<String, Object> getVariables() {\n        return collectVariables(new HashMap<>());\n    }\n\n    @Override\n    public Map<String, VariableInstance> getVariableInstances() {\n        return collectVariableInstances(new HashMap<>());\n    }\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/VariableScopeImpl.java#L50-L86","documentation":"VariableScopeImpl.ensureVariableInstancesInitialized initializes the scope's variable map by loading all variable instances from the database, which requires an active CommandContext. When a variable-scope entity (Execution/Task) is accessed outside engine command execution, initialization is impossible and the engine throws this ActivitiException to prevent silent data corruption. This is the shared guard behind lazy variable loading for all scopes.","triggerScenarios":"Any variable access (getVariable(s), getVariableNames, setVariable, hasVariable) on an ExecutionEntity/TaskEntity when Context.getCommandContext() is null — outside a command: application threads, async jobs started manually, cached detached entities, code between commands.","commonSituations":"Reading process/task variables from a Spring service method invoked outside the engine's command chain with a detached entity; using entities in CompletableFuture/executor threads; custom event listeners invoked without the command interceptor; tests instantiating entities directly.","solutions":["Access variables through the public services (runtimeService.getVariable / taskService.getVariable) which run inside commands","Wrap custom logic in managementService.executeCommand(...) to establish a CommandContext","Do not cache or serialize variable-scope entities; re-fetch them per command","In async code, pass ids/primitive values instead of entity references"],"exampleFix":"// before\nObject v = detachedExecution.getVariable(\"status\"); // throws\n// after\nObject v = runtimeService.getVariable(executionId, \"status\");","handlingStrategy":"type-guard","validationCode":"boolean safe = org.activiti.engine.impl.context.Context.getCommandContext() != null;","typeGuard":"boolean variableAccessAllowed() {\n    return org.activiti.engine.impl.context.Context.getCommandContext() != null;\n}","tryCatchPattern":"try {\n    Object v = execution.getVariable(\"k\");\n} catch (ActivitiException e) {\n    if (e.getMessage().contains(\"lazy loading outside command context\")) {\n        v = runtimeService.getVariable(executionId, \"k\");\n    }\n}","preventionTips":["Always read variables via RuntimeService/TaskService","Wrap custom engine-internal code in executeCommand","Never pass execution/task entities to async threads — pass ids"],"tags":["lazy-loading","command-context","variables","lifecycle"],"backgroundTag":"lazy-loading-outside-command-context","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"}