flowable/flowable-engine · error · ELException
Builder ${clazz} is missing constructor (can't pass features
Error message
Builder ${clazz} is missing constructor (can't pass features) What it means
createTreeBuilder() checks whether a custom TreeBuilder class implements the Builder interface and, if so, requires a constructor taking Feature[] so configured features can be passed. If getConstructor(Feature[].class) yields no such constructor while features were supplied, it throws ELException 'Builder X is missing constructor (can't pass features)'.
Source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/ExpressionFactoryImpl.java:397
* <code>de.odysseus.el.tree.TreeBuilder</code> interface. If the property is not set, a plain
* <code>de.odysseus.el.tree.impl.Builder</code> is used. If the configured class is a subclass
* of <code>de.odysseus.el.tree.impl.Builder</code> and which provides a constructor taking an
* array of <code>Builder.Feature</code>, this constructor will be invoked. Otherwise, the
* default constructor will be used.
*/
protected TreeBuilder createTreeBuilder(Properties properties, Feature... features) {
Class<?> clazz = load(TreeBuilder.class, properties);
if (clazz == null) {
return createDefaultTreeBuilder(features);
}
try {
if (Builder.class.isAssignableFrom(clazz)) {
Constructor<?> constructor = clazz.getConstructor(Feature[].class);
if (constructor == null) {
if (features == null || features.length == 0) {
return TreeBuilder.class.cast(clazz.newInstance());
} else {
throw new ELException("Builder " + clazz + " is missing constructor (can't pass features)");
}
} else {
return TreeBuilder.class.cast(constructor.newInstance((Object) features));
}
} else {
return TreeBuilder.class.cast(clazz.newInstance());
}
} catch (Exception e) {
throw new ELException("TreeBuilder " + clazz + " could not be instantiated", e);
}
}
protected TreeBuilder createDefaultTreeBuilder(Feature... features) {
return new Builder(features);
}
private Class<?> load(Class<?> clazz, Properties properties) {
if (properties != null) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Add a public constructor accepting Feature[] to the custom Builder implementation
- Construct the factory without features if the builder only supports a no-arg constructor
- Use the default Builder (de.odysseus.el.tree.impl.Builder) which supports features
Example fix
// before
public class MyBuilder implements Builder {
public MyBuilder() { }
}
// after
public class MyBuilder implements Builder {
public MyBuilder() { }
public MyBuilder(Feature[] features) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName("com.acme.MyBuilder");
boolean hasFeatureCtor = Arrays.stream(c.getConstructors())
.anyMatch(k -> Arrays.equals(k.getParameterTypes(), new Class[]{Feature[].class}));
if (hasFeaturesRequested && !hasFeatureCtor) {
throw new IllegalStateException("Builder implementing Builder interface needs a public Feature[] constructor");
} Try / catch
try {
ExpressionFactory.newInstance(props, features);
} catch (ELException e) {
if (e.getMessage().contains("is missing constructor")) {
log.error("Add Feature[] constructor to custom Builder or drop features", e);
}
throw e;
} Prevention
- Always provide both no-arg and Feature[] constructors on custom Builders
- Update custom builders after Juel/JUEL-derived API upgrades
- Only pass features when the configured builder supports them
When it happens
Trigger: Constructing ExpressionFactoryImpl (e.g. ExpressionFactory.newInstance with Feature options) where the configured TreeBuilder class implements Builder but only declares a no-arg constructor, and at least one Feature is passed.
Common situations: Plugging in a custom tree builder written for an older Juel version without the Feature[] constructor; calling newInstance with features (like MethodInvocations or VarArgs) against an incompatible custom builder.
Related errors
- TypeConverter ${clazz} could not be instantiated
- TreeBuilder ${clazz} could not be instantiated
- Class ${className} not found
- Cannot read EL properties
- Cannot parse EL property ${PROP_CACHE_SIZE}
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/dd431070333d95a8.
Report an issue: GitHub.