{"record":{"id":"c26a55938beb34da","repo":"flowable/flowable-engine","slug":"cannot-set-value-of-it-s-readonly","errorCode":null,"errorMessage":"Cannot set value of '', it's readonly!","messagePattern":"Cannot set value of '', it's readonly!","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/el/ReadOnlyMapELResolver.java","lineNumber":56,"sourceCode":"        if (base == null) {\n            if (wrappedMap.containsKey(property)) {\n                context.setPropertyResolved(true);\n                return wrappedMap.get(property);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isReadOnly(ELContext context, Object base, Object property) {\n        return true;\n    }\n\n    @Override\n    public void setValue(ELContext context, Object base, Object property, Object value) {\n        if (base == null) {\n            if (wrappedMap.containsKey(property)) {\n                throw new FlowableException(\"Cannot set value of '\" + property + \"', it's readonly!\");\n            }\n        }\n    }\n\n    @Override\n    public Class<?> getCommonPropertyType(ELContext context, Object arg) {\n        return Object.class;\n    }\n\n    @Override\n    public Class<?> getType(ELContext context, Object arg1, Object arg2) {\n        return Object.class;\n    }\n}\n","sourceCodeStart":38,"sourceCodeEnd":71,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/el/ReadOnlyMapELResolver.java#L38-L71","documentation":"ReadOnlyMapELResolver is an EL resolver that exposes a read-only map of variables to Flowable expressions. When an expression attempts to assign a value to a property backed by the wrapped map, the resolver deliberately rejects the write because the exposed map is intended to be immutable within expression evaluation. This guards engine-managed variables from being mutated mid-expression.","triggerScenarios":"Evaluating a Flowable expression that contains an assignment (e.g. ${myVar = 'x'} in a script/expression) where myVar is a key in the read-only map passed to the resolver; calling setValue on the resolver directly with a base of null and a property contained in wrappedMap.","commonSituations":"Using an assignment-style expression in a BPMN service task or listener expression where a read-only variable map (e.g. task/execution variables exposed read-only) is mutated; copying an expression written for a writable variable scope into a read-only context; custom code injecting ReadOnlyMapELResolver and then expecting variable writes to work.","solutions":["Replace the assignment expression with a write through the engine API (e.g. taskService.setVariable / execution.setVariable, or delegateExecution.setVariable in a delegate) instead of mutating variables inside EL.","If writes are required, register a writable resolver/VariableContainer (e.g. VariableContainerWrapper with setVariable support) instead of ReadOnlyMapELResolver.","Set the variable before the expression evaluation, not inside the expression itself.","If the write is intentional inside a script, use a script task with proper variable mapping (in/out mappings) rather than EL assignment."],"exampleFix":"// before\nexpression=\"${orderStatus = 'APPROVED'}\"\n\n// after\n<flowable:field name=\"expression\" expression=\"${orderStatus}\" />\n// plus in a delegate: execution.setVariable(\"orderStatus\", \"APPROVED\");","handlingStrategy":"validation","validationCode":"if (expression != null && expression.matches(\".*[^=!<>+\\-*/]=[^=].*\")) {\n    throw new IllegalArgumentException(\"Expression must not assign to read-only variables: \" + expression);\n}","typeGuard":"boolean isReadOnlyVariable(VariableContainer c, String name) {\n    return c instanceof ReadOnlyMapELResolver; // writes via setVariable are rejected for read-only containers\n}","tryCatchPattern":"try {\n    managementService.executeCommand(new ExpressionSetCommand(expression));\n} catch (FlowableException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"readonly\")) {\n        throw new IllegalArgumentException(\"Assignments are not allowed on read-only variables\");\n    }\n    throw e;\n}","preventionTips":["Never use assignment (=) inside Flowable EL expressions; write variables via the engine API.","Expose only copies/maps intended for reading to expression resolvers.","Use delegateExecution.setVariable in delegates instead of expression-side effects.","Review process definitions for '=' inside ${...} expressions during validation."],"tags":["el","expression","readonly","variables"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}