flowable/flowable-engine · error · FlowableException

deleting historic process instances with related data…

Error message

deleting historic process instances with related data requires CommandExecutor

What it means

Flowable throws this FlowableException from HistoricProcessInstanceQueryImpl.delete when deleting historic process instances with related data but the query was constructed without a CommandExecutor. Deleting related data (tasks, activity data, related content) is executed as separate REQUIRES_NEW commands that need a real command executor; a detached query built manually has none.

Solutions

  1. Obtain the query via processEngine.getHistoryService().createHistoricProcessInstanceQuery() so it carries the engine's CommandExecutor
  2. If constructing manually, use a constructor that accepts the CommandContext or CommandExecutor
  3. In tests, build the query from a real ProcessEngine (or mock the command executor) before calling delete

Example fix

// before
HistoricProcessInstanceQueryImpl q = new HistoricProcessInstanceQueryImpl();
q.delete();
// after
HistoricProcessInstanceQuery q = historyService.createHistoricProcessInstanceQuery()
    .finishedBefore(date);
q.delete();
Defensive patterns

Strategy: validation

Validate before calling

if (query instanceof HistoricProcessInstanceQueryImpl
        && ((HistoricProcessInstanceQueryImpl) query).getCommandExecutor() == null) {
    throw new IllegalStateException("Query must be created via HistoryService to support delete()");
}

Type guard

boolean isDeletableQuery(HistoricProcessInstanceQuery q) {
    return q instanceof HistoricProcessInstanceQueryImpl
        && ((HistoricProcessInstanceQueryImpl) q).getCommandExecutor() != null;
}

Try / catch

try {
    query.delete();
} catch (FlowableException e) {
    throw new IllegalStateException("Rebuild the query via historyService.createHistoricProcessInstanceQuery()", e);
}

Prevention

When it happens

Trigger: Calling query.delete() on a HistoricProcessInstanceQueryImpl created with the no-arg/partial constructor (no commandExecutor injected) instead of obtaining the query from runtimeService.createHistoricProcessInstanceQuery().

Common situations: Unit tests or custom code instantiating HistoricProcessInstanceQueryImpl directly with new and invoking delete; deserializing a query object and calling delete without wiring the engine services.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/32aae0fbc3df9a49. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/HistoricProcessInstanceQueryImpl.java:1296

    @Override
    public void delete() {
        if (commandExecutor != null) {
            commandExecutor.execute(new DeleteHistoricProcessInstancesCmd(this));
        } else {
            new DeleteHistoricProcessInstancesCmd(this).execute(Context.getCommandContext());
        }
    }

    @Override
    @Deprecated
    public void deleteWithRelatedData() {
        if (commandExecutor != null) {
            CommandConfig config = new CommandConfig().transactionRequiresNew();
            commandExecutor.execute(config, new DeleteHistoricProcessInstancesCmd(this));
            commandExecutor.execute(config, new DeleteTaskAndActivityDataOfRemovedHistoricProcessInstancesCmd());
            commandExecutor.execute(config, new DeleteRelatedDataOfRemovedHistoricProcessInstancesCmd());
        } else {
            throw new FlowableException("deleting historic process instances with related data requires CommandExecutor");
        }
    }

    @Override
    public String deleteInParallelUsingBatch(int batchSize, String batchName) {
        return commandExecutor.execute(new DeleteHistoricProcessInstancesUsingBatchesCmd(this, batchSize, batchName, false));
    }

    @Override
    public String deleteSequentiallyUsingBatch(int batchSize, String batchName) {
        return commandExecutor.execute(new DeleteHistoricProcessInstancesUsingBatchesCmd(this, batchSize, batchName, true));
    }

    public String getBusinessKey() {
        return businessKey;
    }

    public String getBusinessKeyLike() {

View on GitHub (pinned to d6d39ce1c6)