flowable/flowable-engine · error · FlowableException

Programmatic error: unsupported transition ${transition} for

Error message

Programmatic error: unsupported transition ${transition} for ${planItemInstanceEntity}

What it means

AsyncLeaveActivePlanItemInstanceJobHandler replays a plan item transition (stored in the job configuration) asynchronously. It only supports a fixed set of transition values (e.g. complete, terminate, fail, exit); anything else reaches the else branch and throws. This is a programming/configuration error: the serialized transition string in the job is not one the handler knows how to plan an operation for.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/job/AsyncLeaveActivePlanItemInstanceJobHandler.java:82

                    CommandContextUtil.getAgenda(commandContext).planExitPlanItemInstanceOperation(planItemInstanceEntity, exitCriterionId, exitType, exitEventType);

                } else if (PlanItemTransition.TERMINATE.equals(transition)) {
                    String exitType = jsonConfiguration.path(OperationSerializationMetadata.FIELD_EXIT_TYPE).stringValue(null);
                    String exitEventType = jsonConfiguration.path(OperationSerializationMetadata.FIELD_EXIT_EVENT_TYPE).stringValue(null);
                    CommandContextUtil.getAgenda(commandContext).planTerminatePlanItemInstanceOperation(planItemInstanceEntity, exitType, exitEventType);

                } else if (PlanItemTransition.FAULT.equals(transition)) {
                    String errorCode = jsonConfiguration.path(OperationSerializationMetadata.FIELD_ERROR_CODE).stringValue(null);
                    if (errorCode != null) {
                        String errorMessage = jsonConfiguration.path(OperationSerializationMetadata.FIELD_ERROR_MESSAGE).stringValue(null);
                        CmmnFault reconstructedError = errorMessage != null ? new CmmnFault(errorCode, errorMessage) : new CmmnFault(errorCode);
                        CommandContextUtil.getAgenda(commandContext).planFailPlanItemInstanceOperation(planItemInstanceEntity, reconstructedError);
                    } else {
                        CommandContextUtil.getAgenda(commandContext).planFailPlanItemInstanceOperation(planItemInstanceEntity);
                    }

                } else {
                    throw new FlowableException("Programmatic error: unsupported transition " + transition + " for " + planItemInstanceEntity);

                }

            } catch (Exception e) {
                throw new FlowableException("Could not deserialize job configuration", e);
            }

        } else {
            throw new FlowableException("Invalid usage of " + TYPE + " job handler, variable scope is of type " + variableScope.getClass());
        }
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Inspect the job's configuration (ACT_RU_JOB CONFIG_ column) and confirm the serialized transition name matches one supported by this handler version.
  2. Upgrade or align the Flowable version of the engine writing the job with the engine executing it, so both agree on transition names.
  3. If you produce these jobs with custom code, only pass supported transition values (complete, terminate, fail, exit).
  4. Check the deserialization code path for corruption — the surrounding catch wraps deserialization in 'Could not deserialize job configuration', so a corrupted config may masquerade as an unsupported transition.

Example fix

// before
cmmnRuntimeService.completePlanItemInstance(planItemInstanceId); // wrong API writing custom transition
// after
// use only supported transition triggers that schedule async leave operations
PlanItemInstance pii = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(id).singleResult();
cmmnRuntimeService.completePlanItemInstance(pii.getId());
Defensive patterns

Strategy: validation

Validate before calling

Set<String> supported = Set.of("complete","terminate","fail","exit");
if (!supported.contains(transition)) throw new IllegalArgumentException("Unsupported transition: " + transition);

Try / catch

try {
    // trigger plan item transition via runtime API
} catch (FlowableException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Programmatic error: unsupported transition")) {
        // inspect job config / upgrade engine, do not blind-retry
        throw new ConfigurationException("Invalid async leave transition", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: execute(job, caseInstanceId, variableScope, commandContext) deserializes the job configuration into a transition string and it is not one of the supported values (complete/terminate/fail/exit); the planFail branch is only entered for failure conditions, and all other unrecognized values fall through to the throw.

Common situations: Job configuration was hand-edited or produced by a different Flowable version writing transition names this build does not understand; a custom command serialized an arbitrary transition into the job configuration; deserialization yields an unexpected value after an upgrade.

Related errors


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