pentaho/pentaho-kettle · error · KettlePluginException

Unexpected error loading class with name

Error message

Unexpected error loading class with name: ${className}

What it means

Generic wrapper thrown at the end of the registry's loadClass(PluginInterface, String): any Exception raised inside the method (ClassNotFound from ucl.loadClass, the class-loader errors above, security exceptions) is rethrown as 'Unexpected error loading class with name: <className>' with the original as cause. It signals that resolving or loading the plugin's class failed for an unclassified reason.

Solutions

  1. Read the chained cause to distinguish ClassNotFoundException (fix class name/jar) from the class-loader errors (fix plugin registration).
  2. Confirm the fully-qualified class name matches the compiled class inside the plugin jar (javap -cp plugin.jar ... or unzip -l).
  3. Ensure the plugin folder/jars are deployed and scanned by the registry at startup.
  4. Update plugin.xml/@Step annotation class attributes after package or class renames.

Example fix

// before
@Step(id = "MyStep", name = "My Step", classNmae = "com.acme.MyStepp")
// after
@Step(id = "MyStep", name = "My Step", className = "com.acme.MyStep")
Defensive patterns

Strategy: try-catch

Validate before calling

String cls = className == null ? plugin.getMainClass() : className;
if (cls == null || cls.isEmpty()) throw new IllegalArgumentException("No class name for plugin " + plugin);
// confirm class presence in the plugin's loader
try { PluginRegistry.getInstance().getClassLoader(plugin).loadClass(cls); } catch (ClassNotFoundException e) { /* resolve before real call */ }

Type guard

boolean pluginClassExists(PluginInterface plugin, String className) {
  try {
    ClassLoader cl = PluginRegistry.getInstance().getClassLoader(plugin);
    cl.loadClass(className);
    return true;
  } catch (Exception e) { return false; }
}

Try / catch

try {
  T obj = PluginRegistry.getInstance().loadClass(plugin, className, MyInterface.class);
} catch (KettlePluginException e) {
  Throwable cause = e.getCause(); // ClassNotFoundException vs loader-missing
  LOG.error("loadClass failed for " + className + ": " + cause, e);
  throw new PluginDeploymentException(className, e);
}

Prevention

When it happens

Trigger: Calling PluginRegistry.loadClass(PluginInterface, String className) where the class name is wrong/absent from the plugin jar, the plugin's class loader is missing (errors 311/312), or Class.forName on a native plugin fails; the inner exception is wrapped and rethrown here.

Common situations: See trigger scenarios.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/c44115d15ec8f48c. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/plugins/PluginRegistry.java:883

            } else {
              ucl = folderBasedClassLoaderMap.get( plugin.getPluginDirectory().toString() );
            }
          }
          if ( ucl != null ) {
            classLoaders.put( plugin, ucl ); // save for later use...
          }
        } finally {
          lock.writeLock().unlock();
        }

        if ( ucl == null ) {
          throw new KettlePluginException( "Unable to find class loader for plugin: " + plugin );
        }
        return (T) ucl.loadClass( className );

      }
    } catch ( Exception e ) {
      throw new KettlePluginException( "Unexpected error loading class with name: " + className, e );
    }
  }

  /**
   * Load the class with a certain name using the class loader of certain plugin.
   *
   * @param plugin    The plugin for which we want to use the class loader
   * @param classType The type of class to load
   * @return the name of the class
   * @throws KettlePluginException In case there is something wrong
   */
  @SuppressWarnings( "unchecked" )
  public <T> T getClass( PluginInterface plugin, T classType ) throws KettlePluginException {
    String className = plugin.getClassMap().get( classType );
    return (T) getClass( plugin, className );
  }

  /**

View on GitHub (pinned to f3058517a1)