{"record":{"id":"cec14eed0bebb704","repo":"pentaho/pentaho-kettle","slug":"pluginregistry002","errorCode":"PLUGINREGISTRY002","errorMessage":"Plugin \"{0}\" is unable to load class {1}","messagePattern":"Plugin \"(.+?)\" is unable to load class (.+?)","errorType":"error_code","errorClass":"KettlePluginClassMapException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/plugins/PluginRegistry.java","lineNumber":485,"sourceCode":"  /**\n   * Load and instantiate the plugin class specified\n   *\n   * @param plugin      the plugin to load\n   * @param pluginClass the class to be loaded\n   * @return The instantiated class\n   * @throws KettlePluginException In case there was a class loading problem somehow\n   */\n  @SuppressWarnings( \"unchecked\" )\n  public <T> T loadClass( PluginInterface plugin, Class<T> pluginClass ) throws KettlePluginException {\n    if ( plugin == null ) {\n      throw new KettlePluginException( BaseMessages.getString(\n          PKG, \"PluginRegistry.RuntimeError.NoValidStepOrPlugin.PLUGINREGISTRY001\" ) );\n    }\n\n    if ( plugin instanceof ClassLoadingPluginInterface ) {\n      T aClass = ( (ClassLoadingPluginInterface) plugin ).loadClass( pluginClass );\n      if ( aClass == null ) {\n        throw new KettlePluginClassMapException( BaseMessages\n            .getString( PKG, \"PluginRegistry.RuntimeError.NoValidClassRequested.PLUGINREGISTRY002\", plugin.getName(),\n                pluginClass.getName() ) );\n      } else {\n        return aClass;\n      }\n    } else {\n      String className = plugin.getClassMap().get( pluginClass );\n      if ( className == null ) {\n        // Look for supplemental plugin supplying extra classes\n        for ( String id : plugin.getIds() ) {\n          try {\n            T aClass = loadClass( plugin.getPluginType(), createSupplemantalKey( plugin.getPluginType().getName(), id ), pluginClass );\n            if ( aClass != null ) {\n              return aClass;\n            }\n          } catch ( KettlePluginException exception ) {\n            // ignore. we'll fall through to the other exception if this loop doesn't produce a return\n          }","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/plugins/PluginRegistry.java#L467-L503","documentation":"In PluginRegistry.loadClass, when the plugin implements ClassLoadingPluginInterface, the class comes from plugin.loadClass(pluginClass). If that returns null, a KettlePluginClassMapException with key ...NoValidClassRequested.PLUGINREGISTRY002 ('Plugin \"X\" is unable to load class Y') is thrown. It means the plugin is registered but its class map holds no class assignable to the requested interface.","triggerScenarios":"loadClass is called with a ClassLoadingPluginInterface plugin whose internal class map has no entry (or a null entry) for the requested pluginClass type — e.g. requesting StepMetaInterface from a plugin that only registered a dialog/main class, or the plugin registered zero matching classes.","commonSituations":"Requesting the wrong interface type for the plugin kind (loading a JobEntry class from a step plugin); plugin annotation/manifest lists no matching class entries; plugin.xml main-class entries filtered out at scan; ClassLoadingPluginInterface.loadClass returns null for an unregistered class loader group.","solutions":["Request a class type the plugin actually provides: match pluginClass to the plugin's kind (StepMetaInterface vs JobEntryInterface).","Check the plugin's annotation/plugin.xml declares the class(es) expected to be mapped (classmappings).","Inspect the plugin's registered class map via the registry to see which interfaces it supports.","If using custom classloader groups, ensure the plugin was registered into the group the loader checks."],"exampleFix":"// before\nPluginInterface plugin = registry.findPluginWithId(JobEntryPluginType.class, \"SHELL\");\nStepMetaInterface meta = registry.loadClass(plugin, StepMetaInterface.class); // wrong type\n\n// after\nPluginInterface plugin = registry.findPluginWithId(JobEntryPluginType.class, \"SHELL\");\nJobEntryInterface jobEntry = registry.loadClass(plugin, JobEntryInterface.class);","handlingStrategy":"try-catch","validationCode":"// Only request class types the plugin declares\nif (plugin instanceof ClassLoadingPluginInterface) {\n  Class<?>[] supported = ((ClassLoadingPluginInterface) plugin).getPluginClasses(); // if exposed\n  // or consult registry class map before requesting\n}","typeGuard":"static <T> T loadIfSupported(PluginRegistry reg, PluginInterface p, Class<T> cls) throws KettlePluginException {\n  T o = reg.loadClass(p, cls); // call only after matching plugin kind to class type\n  return o;\n}","tryCatchPattern":"try {\n  return PluginRegistry.getInstance().loadClass(plugin, pluginClass);\n} catch (KettlePluginClassMapException e) {\n  throw new KettleException(\"Plugin \" + (plugin != null ? plugin.getName() : \"?\")\n      + \" does not map class \" + pluginClass.getName(), e);\n}","preventionTips":["Match the requested interface to the plugin's type (StepMetaInterface for steps, JobEntryInterface for job entries).","Declare all classmappings/classes in the plugin annotation or plugin.xml.","Test loadClass for each expected interface in plugin CI.","Consult the registry's class map when unsure what a plugin provides."],"tags":["kettle","plugins","registry","classloading"],"backgroundTag":"class-not-found","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}