flowable/flowable-engine · error · ELException
error.function.notfound
error.function.notfound
Error message
error.function.notfound
What it means
Tree.bind() asked the FunctionMapper to resolve a function named in the expression and got null back: no Method is registered under that prefix/local name. JUEL throws ELException error.function.notfound naming the function image (e.g. 'my:check').
Solutions
- Register the missing function with the FunctionMapper / Flowable function delegates so resolveFunction returns the Method.
- Fix the function name/prefix typo in the expression.
- Verify at startup that every function used in deployed expressions resolves via the mapper.
- Catch ELException, parse the function image from the message, and report which function is unregistered.
Example fix
// before
// expression uses ${acct:balance(id)} but no 'acct' functions registered -> ELException
// after
functionMapper.mapFunction("acct", "balance",
AccountFunctions.class.getMethod("balance", Long.class)); Defensive patterns
Strategy: validation
Validate before calling
Set<String> registered = loadedFunctionNames(functionMapper); // names your mapper resolves
for (String fn : extractFunctionImages(expression)) { // regex: ([\w]+):([\w]+)\(
if (!registered.contains(fn)) throw new IllegalStateException("Unregistered EL function: " + fn);
} Try / catch
try {
bindings = tree.bind(fnMapper, varMapper, converter);
} catch (ELException e) {
log.error("Unknown EL function in '{}': {}", expression, e.getMessage(), e);
throw new ConfigurationException(e.getMessage(), e);
} Prevention
- Register all custom functions with the FunctionMapper at engine startup.
- Keep a registry/test asserting every function used in deployed expressions resolves.
- Avoid renaming function namespaces without migrating expressions.
- Validate expressions as part of deployment (parse+bind dry run).
When it happens
Trigger: An expression contains ns:name(...) but the FunctionMapper.resolveFunction returns null — the function bean/method is not registered, the prefix is wrong, or the mapper used at bind time differs from the one configured with the functions.
Common situations: Flowable custom function beans not exposed via the function delegate; typo in function name or namespace in the expression; functions available in one engine/module but used in expressions of another; classpath/config changes dropping the function registration.
Related errors
- error.function.nomapper
- Builder is missing constructor (can't pass features)
- Cannot change fixed value with
- Cannot coerce property to array index:
- Cannot coerce property to array index:
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/39186dc6003f8f63.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/tree/Tree.java:124
public Bindings bind(FunctionMapper fnMapper, VariableMapper varMapper, TypeConverter converter) {
Method[] methods = null;
if (!functions.isEmpty()) {
if (fnMapper == null) {
throw new ELException(LocalMessages.get("error.function.nomapper"));
}
methods = new Method[functions.size()];
for (int i = 0; i < functions.size(); i++) {
FunctionNode node = functions.get(i);
String image = node.getName();
Method method = null;
int colon = image.indexOf(':');
if (colon < 0) {
method = fnMapper.resolveFunction("", image);
} else {
method = fnMapper.resolveFunction(image.substring(0, colon), image.substring(colon + 1));
}
if (method == null) {
throw new ELException(LocalMessages.get("error.function.notfound", image));
}
if (node.isVarArgs() && method.isVarArgs()) {
if (method.getParameterTypes().length > node.getParamCount() + 1) {
throw new ELException(LocalMessages.get("error.function.params", image));
}
} else {
if (method.getParameterTypes().length != node.getParamCount()) {
throw new ELException(LocalMessages.get("error.function.params", image));
}
}
methods[node.getIndex()] = method;
}
}
ValueExpression[] expressions = null;
if (identifiers.size() > 0) {
expressions = new ValueExpression[identifiers.size()];
for (int i = 0; i < identifiers.size(); i++) {
IdentifierNode node = identifiers.get(i);View on GitHub (pinned to d6d39ce1c6)