{"record":{"id":"6bed458f8c6e74e2","repo":"flowable/flowable-engine","slug":"elresolver-not-writable-for-type-base-getclass","errorCode":null,"errorMessage":"ELResolver not writable for type '${base.getClass().getName()}'","messagePattern":"ELResolver not writable for type '(.+?)'","errorType":"exception","errorClass":"PropertyNotWritableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/javax/el/OptionalELResolver.java","lineNumber":104,"sourceCode":"        if (base instanceof Optional) {\n            context.setPropertyResolved(base, property);\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p>\n     * If the base object is an {@link Optional} this method always throws a {@link PropertyNotWritableException} since\n     * instances of this resolver are always read-only.\n     */\n    @Override\n    public void setValue(ELContext context, Object base, Object property, Object value) {\n        Objects.requireNonNull(context);\n\n        if (base instanceof Optional) {\n            throw new PropertyNotWritableException(\"ELResolver not writable for type '\" + base.getClass().getName() + \"'\");\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @return If the base object is an {@link Optional} this method always returns {@code true} since instances of this\n     * resolver are always read-only.\n     * <p>\n     * If the base object is not an {@link Optional} then the return value is undefined.\n     */\n    @Override\n    public boolean isReadOnly(ELContext context, Object base, Object property) {\n        Objects.requireNonNull(context);\n\n        if (base instanceof Optional) {\n            context.setPropertyResolved(base, property);\n            return true;","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/javax/el/OptionalELResolver.java#L86-L122","documentation":"OptionalELResolver unwraps java.util.Optional values in EL, but Optionals are immutable value wrappers. setValue throws PropertyNotWritableException whenever the base is an Optional, because setting a property on an Optional has no meaningful semantics.","triggerScenarios":"Calling ValueExpression.setValue (or an assignment expression) where the base resolved to an Optional object, e.g. ${opt.value = x} where opt is Optional<String>; any write targeting an Optional-returning accessor.","commonSituations":"A bean/service method was refactored to return Optional<T>, and existing expressions that assigned into its properties now target an Optional base; developers expect Optional to be transparent for writes as it is for reads.","solutions":["Unwrap the Optional before the write: write to the contained value's holder, not the Optional itself","Refactor so the expression reads from Optional but writes go through a bean setter (e.g. ${bean.name = x} where bean is the unwrapped object)","Replace Optional-returning accessors used in writable EL paths with plain getters","Catch PropertyNotWritableException around the setValue call"],"exampleFix":"// before\nOptional<User> opt = service.findUser();\nel.setValue(ctx, opt, \"name\", \"Ada\"); // throws\n// after\nUser user = service.findUser().orElseThrow();\nel.setValue(ctx, user, \"name\", \"Ada\");","handlingStrategy":"type-guard","validationCode":"if (base instanceof Optional) throw new IllegalArgumentException(\"unwrap Optional before EL write\");","typeGuard":"Object unwrap(Object o){ return o instanceof Optional<?> opt ? opt.orElse(null) : o; }","tryCatchPattern":"try { expr.setValue(ctx, base, value); } catch (PropertyNotWritableException e) { /* base was an Optional; unwrap and retry on the contained value */ }","preventionTips":["Unwrap Optionals before passing to EL writes","Keep Optional only for read paths","Document that Optional-returning getters are read-only in EL"],"tags":[],"backgroundTag":null,"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"}