flowable/flowable-engine · error · ELException

error.function.access

Error message

error.function.access

What it means

AstFunction.eval() invokes the bound EL function via reflection. If the reflected Method is inaccessible from the EL context (IllegalAccessException — e.g. the method or its class is not public, or package-private and blocked by module/access restrictions), the failure is wrapped in ELException with message 'error.function.access: <functionName>'.

Source

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

			} else {
				for (int i = 0; i < params.length; i++) {
					Object param = getParam(i).eval(bindings, context);
					if (param != null || types[i].isPrimitive()) {
						params[i] = bindings.convert(param, types[i]);
					}
				}
			}
		}
		return method.invoke(base, params);
	}

	@Override 
	public Object eval(Bindings bindings, ELContext context) {
		Method method = bindings.getFunction(index);
		try {
			return invoke(bindings, context, null, method);
		} catch (IllegalAccessException e) {
			throw new ELException(LocalMessages.get("error.function.access", name), e);
		} catch (InvocationTargetException e) {
			throw new ELException(LocalMessages.get("error.function.invocation", name), e.getCause());
		}
	}

	@Override
	public String toString() {
		return name;
	}	

	@Override 
	public void appendStructure(StringBuilder b, Bindings bindings) {
		b.append(bindings != null && bindings.isFunctionBound(index) ? "<fn>" : name);
		params.appendStructure(b, bindings);
	}

	@Override
	public int getIndex() {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the function method and its declaring class public.
  2. Verify the function mapping in the ExpressionFactory/config points at the correct class and method name.
  3. With JPMS, open/export the package to the EL implementation module.
  4. Catch ELException around expression evaluation and log getRootCause()/getCause() (the IllegalAccessException) to identify the inaccessible method.

Example fix

// before
class Fns { static boolean check(int x) { ... } } // package-private
// after
public class Fns { public static boolean check(int x) { ... } }
Defensive patterns

Strategy: try-catch

Validate before calling

// verify function accessibility before registering
Method m = clazz.getMethod(name, paramTypes);
if (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(clazz.getModifiers()))
    throw new IllegalArgumentException("EL function must be public: " + name);

Try / catch

try {
    return valueExpression.getValue(context);
} catch (ELException e) {
    Throwable root = e.getCause();
    if (root instanceof IllegalAccessException)
        throw new IllegalStateException("EL function not accessible: " + root.getMessage(), root);
    throw e;
}

Prevention

When it happens

Trigger: Registering an EL function whose implementing Method is non-public (private/protected/package-private), or public method in a non-exported package (JPMS), then evaluating an expression that calls the function (e.g. ${myfn:check(x)}).

Common situations: Function libraries registered via ExpressionFactory/function mappings pointing at utility classes with non-public methods; Java 9+ module encapsulation hiding reflection targets; shaded/relocated classes in fat jars breaking accessibility.

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/3b88f2d26161f309. Report an issue: GitHub.