pentaho/pentaho-kettle · error · KettleException

ScriptMeta.Exception.UnableToLoadAdditionalClass

Error message

ScriptMeta.Exception.UnableToLoadAdditionalClass

What it means

LoadAdditionalClass() creates a KettleURLClassLoader over a jar URL and calls kl.loadClass(strClassName). If the jar can't be opened as a URL, is unreadable, or the class isn't found, the exception is wrapped in KettleException 'UnableToLoadAdditionalClass'.

Solutions

  1. Confirm the jar path used for the URL exists and is readable by the JVM user.
  2. Use the fully qualified class name (com.example.MyHelper, not MyHelper).
  3. Verify the class exists in the jar: jar tf MyHelper.jar | grep MyHelper.
  4. Check the nested 'Caused by' — ClassNotFoundException vs MalformedURLException points to different fixes.

Example fix

// before
LoadAdditionalClass(path, "MyHelper");
// after
LoadAdditionalClass(path, "com.example.helper.MyHelper");
Defensive patterns

Strategy: validation

Validate before calling

// Verify class presence in jar before loading
jar tf MyHelper.jar | grep com/example/helper/MyHelper.class

Try / catch

try { Class<?> c = LoadAdditionalClass(jarPath, className); }
catch (KettleException e) {
  if (e.getCause() instanceof ClassNotFoundException) { /* fix className or jar */ }
  if (e.getCause() instanceof java.net.MalformedURLException) { /* fix jar path */ }
}

Prevention

When it happens

Trigger: Loading a Script step's additional class when strJar points to a nonexistent/unreadable file or strClassName is not present in the jar.

Common situations: Plugin jar not deployed to plugins/steps/ScriptValues_mod/; jar corrupted; package name in class string wrong (e.g. missing fully-qualified name); running on a different OS with case-sensitive paths.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/script/ScriptMeta.java:842

      }
    } catch ( Exception e ) {
      throw new KettleException( BaseMessages.getString(
        PKG, "ScriptMeta.Exception.UnableToParseXMLforAdditionalClasses" ), e );
    }
  }

  private static Class<?> LoadAdditionalClass( String strJar, String strClassName ) throws KettleException {
    try {
      Thread t = Thread.currentThread();
      ClassLoader cl = t.getContextClassLoader();
      URL u = new URL( "jar:file:" + strJar + "!/" );
      // We never know what else the script wants to load with the class loader, so lets not close it just like that.
      @SuppressWarnings( "resource" )
      KettleURLClassLoader kl = new KettleURLClassLoader( new URL[] { u }, cl );
      Class<?> toRun = kl.loadClass( strClassName );
      return toRun;
    } catch ( Exception e ) {
      throw new KettleException(
        BaseMessages.getString( PKG, "ScriptMeta.Exception.UnableToLoadAdditionalClass" ), e );
    }
  }

  public ScriptAddClasses[] getAddClasses() {
    return additionalClasses;
  }

  public boolean supportsErrorHandling() {
    return true;
  }

  /**
   * @return the replace
   */
  public boolean[] getReplace() {
    return replace;
  }

View on GitHub (pinned to f3058517a1)