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

  1. Verify driverClassName matches the actual class inside the JAR (jar tf <jar> | grep -i driver)
  2. Confirm the class implements java.sql.Driver and has a public no-arg constructor
  3. Check the JAR's class file version is compatible with your JVM
  4. 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

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


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)