flowable/flowable-engine · error · ELException

error.property.method.access

Error message

error.property.method.access

What it means

ELException (error.property.method.access) thrown when Method.invoke raises IllegalAccessException during expression invocation. The method was found but the reflective call is not permitted — typically because the method or its declaring class is not accessible from the caller's context.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/tree/impl/ast/AstProperty.java:215

		return new MethodInfo(method.getName(), method.getReturnType(), paramTypes);
	}

	@Override
	public Object invoke(Bindings bindings, ELContext context, Class<?> returnType, Class<?>[] paramTypes, Object[] paramValues) {
		Object base = prefix.eval(bindings, context);
		if (base == null) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.base.null", prefix));
		}
		Object property = getProperty(bindings, context);
		if (property == null && strict) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.method.notfound", "null", base));
		}
		String name = bindings.convert(property, String.class);
		Method method = findMethod(name, base.getClass(), returnType, paramTypes);
		try {
			return method.invoke(base, paramValues);
		} catch (IllegalAccessException e) {
			throw new ELException(LocalMessages.get("error.property.method.access", name, base.getClass()), e);
		} catch (IllegalArgumentException e) {
			throw new ELException(LocalMessages.get("error.property.method.invocation", name, base.getClass()), e);
		} catch (InvocationTargetException e) {
			throw new ELException(LocalMessages.get("error.property.method.invocation", name, base.getClass()), e.getCause());
		}
	}

	@Override
	public AstNode getChild(int i) {
		return i == 0 ? prefix : null;
	}
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Expose the implementation class publicly or invoke through a public interface type.
  2. Add module exports/opens (module-info: exports/opens the package) if using JPMS.
  3. Wrap the logic in a public delegate bean and call that from EL.
  4. Remove/relax the SecurityManager restriction or grant ReflectPermission.

Example fix

// before
// module-info.java lacks: exports com.example.services;
// after
module com.example { exports com.example.services; }
Defensive patterns

Strategy: try-catch

Validate before calling

Class<?> c = base.getClass();
if (!java.lang.reflect.Modifier.isPublic(c.getModifiers())) {
    throw new IllegalStateException("EL base class must be public: " + c);
}

Type guard

boolean reflectivelyCallable(Object o, String name) {
    try {
        return o != null && o.getClass().getMethod(name).canAccess(o);
    } catch (NoSuchMethodException e) { return false; }
}

Try / catch

try {
    result = expr.invoke(ctx);
} catch (jakarta.el.ELException e) {
    if (e.getCause() instanceof IllegalAccessException) {
        log.warn("EL method inaccessible: {}", e.getMessage());
        throw new IllegalStateException("expose method publicly", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: invoke() on ${obj.method()} where the resolved Method cannot be legally invoked reflectively (non-public class implementing a public interface, restricted module/package, SecurityManager denial).

Common situations: Invoking methods on non-public classes in JPMS modules without opens/exports; calling interface methods whose implementation class is package-private; sandboxed environments restricting reflection.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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