pentaho/pentaho-kettle · error · KettleDatabaseException
DynamicDriverCache: failed to load driver '" +…
Error message
DynamicDriverCache: failed to load driver '" + driverClassName + "' from '" + jarAbsolutePath + "': " + e.getMessage()
What it means
DynamicDriverCache.getOrLoadDriver() loads a JDBC driver class from a JAR using a ChildFirstURLClassLoader and instantiates it. If class loading, class cast to java.sql.Driver, or reflective instantiation fails, the loader is closed and a KettleDatabaseException naming the driver class and JAR path is thrown.
Solutions
- Verify driverClassName matches the actual class inside the JAR (jar tf <jar> | grep -i driver)
- Confirm the class implements java.sql.Driver and has a public no-arg constructor
- Check the JAR's class file version is compatible with your JVM
- Re-download/replace the JAR in case it is corrupt
Example fix
// before Driver d = cache.getOrLoadDriver( "/opt/jars/mysql.jar", "com.mysql.jdbc.Driver" ); // after (Connector/J 8.x) Driver d = cache.getOrLoadDriver( "/opt/jars/mysql.jar", "com.mysql.cj.jdbc.Driver" );
Defensive patterns
Strategy: try-catch
Validate before calling
File jar = new File( jarAbsolutePath );
if ( !jar.isFile() ) throw new IllegalStateException( "missing driver JAR: " + jar );
try ( JarFile jf = new JarFile( jar ) ) {
if ( jf.getJarEntry( driverClassName.replace( '.', '/' ) + ".class" ) == null )
throw new IllegalStateException( driverClassName + " not in " + jar );
} Type guard
boolean isDriverClass( Class<?> c ) { return java.sql.Driver.class.isAssignableFrom( c ); } Try / catch
try { driver = cache.getOrLoadDriver( jar, cls ); } catch ( KettleDatabaseException e ) { log.error( "driver load failed for " + cls + ": " + e.getCause(), e ); } Prevention
- Keep a mapping table of databaseType → driverClassName up to date
- Verify JARs are complete (checksum) after download
- Align JVM version with driver JAR requirements
When it happens
Trigger: Calling getOrLoadDriver with a JAR that does not contain driverClassName; the class exists but does not implement java.sql.Driver; the driver class has no no-arg constructor or its constructor throws; the JAR is corrupt/incompatible with the JVM.
Common situations: Wrong driverClassName configured for the JAR (e.g. MySQL Connector 8.x moved to com.mysql.cj.jdbc.Driver); JAR built for a newer Java version than the runtime; truncated or corrupted downloaded JAR.
Related errors
- Database.Exception.UnableToGetMetadata
- Error connecting to database
- JdbcDriverResolver: driver JAR '" + driverId + ".jar" + "'…
- JdbcDriverResolver: driverId must not be null or blank
- JdbcDriverResolver: JAR not found: " + jarAbsolutePath
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/0fd583d37fef8193.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/core/database/DynamicDriverCache.java:227
}
} finally {
rwLock.readLock().unlock();
}
// 2. Expensive work outside any lock — NFS/SMB reads happen here.
log.logBasic( "DynamicDriverCache: loading " + driverClassName + " from " + jarAbsolutePath + ", " + extraJarsAbsolutePath );
List<URL> urls = JdbcDriverResolver.buildUrlList( jarAbsolutePath, extraJarsAbsolutePath );
ChildFirstURLClassLoader loader = null;
CacheEntry entry;
try {
loader = new ChildFirstURLClassLoader( urls.toArray( new URL[ 0 ] ), Database.class.getClassLoader() );
Class<?> driverClass = loader.loadClass( driverClassName );
Driver driver = (Driver) driverClass.getDeclaredConstructor().newInstance();
entry = new CacheEntry( loader, driver );
} catch ( Exception e ) {
closeSilently( loader );
throw new KettleDatabaseException(
"DynamicDriverCache: failed to load driver '" + driverClassName
+ "' from '" + jarAbsolutePath + "': " + e.getMessage(), e );
}
// 3. Write lock — double-check, insert, collect oldest entry if over capacity.
Driver driverToReturn;
rwLock.writeLock().lock();
try {
CacheEntry existing = cache.get( key );
if ( existing != null ) {
// Another thread beat us to it — discard our copy, return the winner's Driver.
closeSilently( loader );
log.logDebug( "DynamicDriverCache: concurrent load race, discarding duplicate for " + driverClassName );
return existing.driver;
}
cache.put( key, entry );
log.logBasic( "DynamicDriverCache: cached driver " + driverClassName + " @ " + jarAbsolutePath );
driverToReturn = entry.driver;View on GitHub (pinned to f3058517a1)