flowable/flowable-engine · error · FlowableIllegalArgumentException

Child task variables can only be set when starting a plan it

Error message

Child task variables can only be set when starting a plan item instance

What it means

PlanItemInstanceTransitionBuilderImpl.validateChildTaskVariablesNotSet() throws FlowableIllegalArgumentException when childTaskVariables were set but the transition is not a start operation. Child task variables are only meaningful when starting a plan item instance that will create a child task; any other transition (trigger, enable, disable, suspend, resume, terminate) rejects them.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceTransitionBuilderImpl.java:236

    }

    @Override
    public void completeStage() {
        validateChildTaskVariablesNotSet();
        commandExecutor.execute(new CompleteStagePlanItemInstanceCmd(planItemInstanceId, variables, formVariables, formOutcome, 
                formInfo, localVariables, transientVariables, false));
    }

    @Override
    public void forceCompleteStage() {
        validateChildTaskVariablesNotSet();
        commandExecutor.execute(new CompleteStagePlanItemInstanceCmd(planItemInstanceId, variables, formVariables, formOutcome, 
                formInfo, localVariables, transientVariables, true));
    }

    protected void validateChildTaskVariablesNotSet() {
        if (childTaskVariables != null) {
            throw new FlowableIllegalArgumentException("Child task variables can only be set when starting a plan item instance");
        }

        if (childTaskFormInfo != null) {
            throw new FlowableIllegalArgumentException("Child form variables can only be set when starting a plan item instance");
        }
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Use start() when child task variables are set — that is the only transition that accepts them.
  2. Remove the childTaskVariables(...) call for non-start transitions and pass plain variables(variables) instead.
  3. Branch on the intended operation before populating the builder.

Example fix

// before
builder.childTaskVariables(childVars).trigger(); // invalid transition
// after
builder.childTaskVariables(childVars).start();
// or, for trigger:
builder.variables(vars).trigger();
Defensive patterns

Strategy: validation

Validate before calling

if (childTaskVariables != null && operation != Operation.START) { childTaskVariables = null; }

Try / catch

try { builder.trigger(); } catch (FlowableIllegalArgumentException e) { throw new IllegalStateException("childTaskVariables require start()", e); }

Prevention

When it happens

Trigger: Calling a transition builder that did planItemInstanceBuilder.childTaskVariables(...) and then invoking trigger()/enable()/disable()/suspend()/resume()/terminate() instead of start().

Common situations: Generic transition dispatch code that sets child task variables for every operation; copying a start() example and swapping the terminal operation for trigger().

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