{"record":{"id":"163447405cfcb1a4","repo":"flowable/flowable-engine","slug":"resolver-is-read-only","errorCode":null,"errorMessage":"Resolver is read only!","messagePattern":"Resolver is read only!","errorType":"exception","errorClass":"PropertyNotWritableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/util/RootPropertyResolver.java","lineNumber":96,"sourceCode":"\t\t\tif (!isProperty((String) property)) {\n\t\t\t\tthrow new PropertyNotFoundException(\"Cannot find property \" + property);\n\t\t\t}\n\t\t\treturn getProperty((String) property);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isReadOnly(ELContext context, Object base, Object property) {\n\t\treturn resolve(context, base, property) ? readOnly : false;\n\t}\n\n\t@Override\n\tpublic void setValue(ELContext context, Object base, Object property, Object value)\n\t\t\tthrows PropertyNotWritableException {\n\t\tif (resolve(context, base, property)) {\n\t\t\tif (readOnly) {\n\t\t\t\tthrow new PropertyNotWritableException(\"Resolver is read only!\");\n\t\t\t}\n\t\t\tsetProperty((String) property, value);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {\n\t\tif (resolve(context, base, method)) {\n\t\t\tthrow new NullPointerException(\"Cannot invoke method \" + method + \" on null\");\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get property value\n\t * \n\t * @param property\n\t *            property name","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/util/RootPropertyResolver.java#L78-L114","documentation":"RootPropertyResolver can be constructed in read-only mode; any attempt to write a property through it then throws PropertyNotWritableException('Resolver is read only!'). This protects root context objects (e.g. process variables views) from mutation via EL.","triggerScenarios":"Calling ELResolver.setValue / ValueExpression.setValue where the resolved base is a root object handled by a read-only RootPropertyResolver (created with readOnly=true).","commonSituations":"Assigning to root properties in expressions evaluated with a read-only resolver; scripts that try to persist values by writing back to the root context instead of using engine APIs.","solutions":["Construct the RootPropertyResolver with readOnly=false (new RootPropertyResolver(map, false)) if writes are intended.","Write values through the engine API (setVariable) instead of EL assignment.","Check resolver.isReadOnly() / ValueExpression.isReadOnly() before attempting setValue."],"exampleFix":"// before\nRootPropertyResolver resolver = new RootPropertyResolver(map); // readOnly default true\nresolver.setValue(ctx, root, \"count\", 3); // throws\n// after\nRootPropertyResolver resolver = new RootPropertyResolver(map, false);","handlingStrategy":"validation","validationCode":"if (resolver.isReadOnly()) { throw new IllegalStateException(\"Root resolver is read-only; use engine APIs to persist values\"); }","typeGuard":null,"tryCatchPattern":"try { resolver.setValue(ctx, base, prop, value); } catch (PropertyNotWritableException e) { log.error(\"Resolver is read-only: {}\", e.getMessage()); }","preventionTips":["Construct with readOnly=false only when writes are required","Persist state via runtimeService/delegate setVariable instead of EL writes","Document read-only resolvers for script authors"],"tags":["el","resolver","readonly","write"],"backgroundTag":"permission-denied","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"}