flowable/flowable-engine · error · ELException

Class ${className} not found

Error message

Class ${className} not found

What it means

load() looks up a class name from the factory properties (keyed by the requesting class's name) and loads it via the context class loader or Class.forName. A ClassNotFoundException is wrapped as ELException 'Class X not found' — the property names a class that does not exist on the classpath.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/ExpressionFactoryImpl.java:427

	protected TreeBuilder createDefaultTreeBuilder(Feature... features) {
		return new Builder(features);
	}

	private Class<?> load(Class<?> clazz, Properties properties) {
		if (properties != null) {
			String className = properties.getProperty(clazz.getName());
			if (className != null) {
				ClassLoader loader;
				try {
					loader = Thread.currentThread().getContextClassLoader();
				} catch (Exception e) {
					throw new ELException("Could not get context class loader", e);
				}
				try {
					return loader == null ? Class.forName(className) : loader.loadClass(className);
				} catch (ClassNotFoundException e) {
					throw new ELException("Class " + className + " not found", e);
				} catch (Exception e) {
					throw new ELException("Class " + className + " could not be instantiated", e);
				}
			}
		}
		return null;
	}

	@Override
	public final <T> T coerceToType(Object obj, Class<T> targetType) {
		return converter.convert(obj, targetType);
	}

	@Override
	public final ObjectValueExpression createValueExpression(Object instance, Class<?> expectedType) {
		return new ObjectValueExpression(converter, instance, expectedType);
	}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Correct the fully-qualified class name in the properties to a class present on the classpath
  2. Add the jar/module containing the named class to the runtime classpath
  3. Remove the property entry to fall back to the built-in default implementation

Example fix

// before
de.odysseus.el.TypeConverter = com.acme.OldConverter

// after
de.odysseus.el.TypeConverter = com.acme.NewConverter
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check every class name referenced by factory properties
String name = props.getProperty("de.odysseus.el.TypeConverter");
if (name != null && Class.forName(name, false, getClass().getClassLoader()) == null) {
    throw new IllegalStateException("Configured class not on classpath: " + name);
}

Try / catch

try {
    ExpressionFactory factory = new ExpressionFactoryImpl(props);
} catch (ELException e) {
    if (e.getCause() instanceof ClassNotFoundException) {
        log.error("Add missing jar or correct the FQN in EL properties: {}", e.getCause().getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: ExpressionFactoryImpl construction where a properties key (e.g. the TypeConverter or TreeBuilder property key) maps to a class name that is misspelled, not deployed, or in a jar missing from the runtime classpath.

Common situations: Renamed/moved class after a library upgrade while properties file still references the old FQN; missing dependency jar in the deployed WAR; typo in fully-qualified class name in juel/EL properties.

Related errors


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