flowable/flowable-engine · error · ELException

Error reading ' ' on type

Error message

Error reading '${propertyName}' on type '${base.getClass().getName()}'

What it means

RecordELResolver.getValue found the record's accessor method via reflection but Method.invoke failed (IllegalAccessException, InvocationTargetException from an accessor that threw, etc.). It rethrows as ELException with the property and type named for diagnosis.

Solutions

  1. Inspect the cause (ReflectiveOperationException) for the real failure and fix the accessor logic
  2. Ensure the record class/package is exported/opened to the EL engine module under JPMS
  3. Null-check inputs used by the accessor so it cannot throw during evaluation
  4. Catch ELException, unwrap getCause(), and surface the root cause

Example fix

// before
public record Cfg(String value) { public String value() { return value.trim(); } } // NPE if value null
// after
public record Cfg(String value) { public String value() { return value == null ? "" : value.trim(); } }
Defensive patterns

Strategy: try-catch

Validate before calling

if (value == null && accessorPerformsOperations) throw new IllegalStateException("accessor would NPE");

Try / catch

try { return expr.getValue(ctx); } catch (ELException e) { Throwable root = e.getCause(); log.error("Accessor failed on {} : {}", type, root, root); throw root instanceof RuntimeException r ? r : new IllegalStateException(root); }

Prevention

When it happens

Trigger: The record accessor itself throws an exception during evaluation; the accessor is public on the method but its declaring class is not accessible to the caller (illegal access); the resolved method's body throws (e.g. validation in a compact constructor-dependent accessor).

Common situations: Accessor lazily computes and fails (NPE inside accessor); records from another module/package not opened to the EL module (JPMS strong encapsulation); a nested exception in a record built from external data.


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

Appendix: source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/javax/el/RecordELResolver.java:79

        if (base instanceof Record && property != null) {
            context.setPropertyResolved(base, property);

            String propertyName = property.toString();

            Method method;
            try {
                method = base.getClass().getMethod(propertyName);
            } catch (NoSuchMethodException nsme) {
                throw new PropertyNotFoundException("Property '" + propertyName + "' not found on type '" + base.getClass().getName() + "'", nsme);
            }

            if (Modifier.isPublic(method.getModifiers())) {
                method.trySetAccessible();
            }
            try {
                return method.invoke(base);
            } catch (ReflectiveOperationException e) {
                throw new ELException("Error reading '" + propertyName + "' on type '" + base.getClass().getName() + "'", e);
            }
        }
        return null;
    }

    /**
     * If the base object is an instance of {@link Record}, always returns {@code null} since {@link Record}s are always
     * read-only.
     * <p>
     * If the base object is an instance of {@link Record}, the {@code propertyResolved} property of the provided
     * {@link ELContext} must be set to {@code true} by this resolver before returning. If this property is not {@code
     * true} after this method is called, the caller should ignore the return value.
     *
     * @param context The context of this evaluation.
     * @param base The {@link Record} to analyze.
     * @param property The name of the property to analyze. Will be coerced to a String.
     * @return Always {@code null}
     * @throws NullPointerException if the provided {@link ELContext} is {@code null}.

View on GitHub (pinned to d6d39ce1c6)