flowable/flowable-engine · error · UnsupportedOperationException

Setting transient variable is not supported for read only de

Error message

Setting transient variable is not supported for read only delegate execution

What it means

ReadOnlyDelegatePlanItemInstance is a default-implementation wrapper handed to CMMN delegates that must not mutate case state. All mutating methods, including setTransientVariable, throw UnsupportedOperationException by design to signal that writes are illegal in this execution context.

Source

Thrown at modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/delegate/ReadOnlyDelegatePlanItemInstance.java:43

    PlanItem getPlanItem();

    default PlanItemDefinition getPlanItemDefinition() {
        PlanItem planItem = getPlanItem();
        if (planItem != null) {
            return planItem.getPlanItemDefinition();
        }

        return null;
    }

    @Override
    default void setVariable(String variableName, Object variableValue) {
        throw new UnsupportedOperationException("Setting variable is not supported for read only delegate execution");
    }

    @Override
    default void setTransientVariable(String variableName, Object variableValue) {
        throw new UnsupportedOperationException("Setting transient variable is not supported for read only delegate execution");
    }

    @Override
    default void setLocalizedName(String localizedName) {
        throw new UnsupportedOperationException("Setting localized name is not supported for read only delegate execution");
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Remove the setTransientVariable call when running in a read-only context
  2. Detect read-only instances before writing and skip or buffer the write
  3. Use a different extension point (e.g. a non-read-only delegate/listener) if the write is required
  4. Pass required data via the delegate's input mappings/field injections instead of transient variables

Example fix

// before
public void notify(DelegatePlanItemInstance planItemInstance) {
    planItemInstance.setTransientVariable("result", compute());
}
// after
public void notify(DelegatePlanItemInstance planItemInstance) {
    if (!(planItemInstance instanceof ReadOnlyDelegatePlanItemInstance)) {
        planItemInstance.setTransientVariable("result", compute());
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

boolean canWrite = !(planItemInstance instanceof ReadOnlyDelegatePlanItemInstance);

Type guard

if (planItemInstance instanceof ReadOnlyDelegatePlanItemInstance readOnly) { /* read-only: skip writes */ }

Try / catch

try { planItemInstance.setTransientVariable(name, value); } catch (UnsupportedOperationException e) { log.warn("Read-only context, skipped transient variable {}", name); }

Prevention

When it happens

Trigger: Calling planItemInstance.setTransientVariable(name, value) from inside a delegate (e.g. a TaskListener or PlanItemJavaDelegate) that received a read-only delegate plan item instance, typically in read-only listener contexts such as CMMN case lifecycle listeners.

Common situations: Reusing a variable-writing delegate written for normal delegates in a read-only listener; copying BPMN task code into a CMMN lifecycle listener; frameworks injecting read-only instances for audit/history listeners.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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