flowable/flowable-engine · error · FlowableIllegalArgumentException
ExecuteDecisionContext is null
Error message
ExecuteDecisionContext is null
What it means
PersistHistoricDecisionExecutionCmd persists a historic decision execution record using the ExecuteDecisionContext supplied to its constructor. If that context is null the command throws FlowableIllegalArgumentException, because it cannot derive the decision execution data to store. It is invoked from the ExecuteDecisionActivityBehavior during DMN rule execution.
Solutions
- Never construct PersistHistoricDecisionExecutionCmd manually; let the ExecuteDecisionActivityBehavior supply a properly built context
- If writing custom behavior, build an ExecuteDecisionContext with non-null decisionExecution data before persisting history
- Check for overrides or forks of ExecuteDecisionActivityBehavior that skip context creation
Example fix
// before new PersistHistoricDecisionExecutionCmd(null).execute(commandContext); // after ExecuteDecisionContext ctx = new ExecuteDecisionContext(decisionExecution); new PersistHistoricDecisionExecutionCmd(ctx).execute(commandContext);
Defensive patterns
Strategy: validation
Validate before calling
if (ctx == null || ctx.getDecisionExecution() == null) {
throw new IllegalStateException("ExecuteDecisionContext must be built before persisting history");
} Type guard
boolean hasContext(ExecuteDecisionContext c) { return c != null && c.getDecisionExecution() != null; } Try / catch
try {
behavior.execute(execution);
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().contains("ExecuteDecisionContext is null")) {
log.error("Decision context missing during history persist", e);
}
throw e;
} Prevention
- Do not construct engine-internal commands directly; rely on ExecuteDecisionActivityBehavior
- When subclassing decision behavior, always build the context before invoking the persist command
- Enable history only after verifying the decision pipeline sets up its context
When it happens
Trigger: Executing a DMN decision through the engine (e.g. DmnEngine.executeDecide or an ExecuteDecisionActivityBehavior flow) where the internally constructed ExecuteDecisionContext is null — typically after a failed or misconfigured decision execution setup.
Common situations: Custom code building PersistHistoricDecisionExecutionCmd manually with a null context; engine bugs or subclass overrides that leave the context unset when history is enabled.
Related errors
- Decision table id is null
- decisionTableId is null
- Deployment id is null
- deploymentId is null
- deploymentId is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ceba7cbf5e20c8b0.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/cmd/PersistHistoricDecisionExecutionCmd.java:44
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
/**
* @author Yvo Swillens
*/
public class PersistHistoricDecisionExecutionCmd implements Command<Void> {
protected ExecuteDecisionContext executeDecisionContext;
public PersistHistoricDecisionExecutionCmd(ExecuteDecisionContext executeDecisionContext) {
this.executeDecisionContext = executeDecisionContext;
}
@Override
public Void execute(CommandContext commandContext) {
if (executeDecisionContext == null) {
throw new FlowableIllegalArgumentException("ExecuteDecisionContext is null");
}
DmnEngineConfiguration engineConfiguration = CommandContextUtil.getDmnEngineConfiguration();
if (engineConfiguration.isHistoryEnabled()) {
HistoricDecisionExecutionEntityManager historicDecisionExecutionEntityManager = engineConfiguration.getHistoricDecisionExecutionEntityManager();
HistoricDecisionExecutionEntity decisionExecutionEntity = historicDecisionExecutionEntityManager.create();
decisionExecutionEntity.setDecisionDefinitionId(executeDecisionContext.getDecisionId());
decisionExecutionEntity.setDeploymentId(executeDecisionContext.getDeploymentId());
decisionExecutionEntity.setStartTime(executeDecisionContext.getDecisionExecution().getStartTime());
decisionExecutionEntity.setEndTime(executeDecisionContext.getDecisionExecution().getEndTime());
decisionExecutionEntity.setInstanceId(executeDecisionContext.getInstanceId());
decisionExecutionEntity.setExecutionId(executeDecisionContext.getExecutionId());
decisionExecutionEntity.setActivityId(executeDecisionContext.getActivityId());
decisionExecutionEntity.setScopeType(executeDecisionContext.getScopeType());
decisionExecutionEntity.setTenantId(executeDecisionContext.getTenantId());
Boolean failed = executeDecisionContext.getDecisionExecution().isFailed();
View on GitHub (pinned to d6d39ce1c6)