pentaho/pentaho-kettle · error · KettleException
ScriptValuesMetaMod.Exception.UnableToParseXMLforAdditionalC…
Error message
ScriptValuesMetaMod.Exception.UnableToParseXMLforAdditionalClasses
What it means
When building a ScriptValuesMod step from XML (constructor that reads additional script classes), any exception while loading the configured additional JARs/classes is wrapped in a KettleException with this message. It indicates the <add_classes> section of the step XML could not be turned into live additionalClasses objects. The root cause is attached.
Solutions
- Read the nested cause to identify the exact failure (FileNotFound vs ClassNotFoundException vs InstantiationError)
- Deploy the referenced additional JAR to <kettle-dir>/plugins/steps/ScriptValues_mod/ and verify strActPath resolves to the real Kettle home
- Check the class name stored in the step XML matches a class actually present in the JAR
- Ensure the additional class has a public no-argument constructor that does not throw
- Re-export the transformation XML after fixing the additional-classes configuration
Example fix
// before: class missing on this machine -> wrapped as UnableToParseXMLforAdditionalClasses
// <add_classes><jar>myhelper.jar</jar><class>com.x.Helper</class>...
// after: verify before loading
File jar = new File(strActPath + "/plugins/steps/ScriptValues_mod/myhelper.jar");
if (!jar.exists()) throw new KettleException("Deploy myhelper.jar to " + jar.getPath()); Defensive patterns
Strategy: validation
Validate before calling
File dir = new File(System.getProperty("KETTLE_HOME", "."), "plugins/steps/ScriptValues_mod");
for (String jar : configuredJars) {
if (!new File(dir, jar).exists())
throw new KettleException("Missing additional jar: " + jar + " in " + dir);
} Try / catch
try {
stepMeta = new StepMeta(xStepNode, transMeta);
} catch (KettleException e) {
if (e.getMessage().contains("UnableToParseXMLforAdditionalClasses")) {
logError("Additional classes load failed: " + e.getCause(), e);
// deploy JARs and retry, or strip add_classes from the XML
} else throw e;
} Prevention
- Deploy all additional JARs to every environment under plugins/steps/ScriptValues_mod/
- Prefer built-in scripting capabilities over custom additional classes to reduce deploy coupling
- Keep additional classes with public no-arg constructors and no side effects in them
- Pin JDK version used to compile additional JARs to the runtime JDK or lower
- Validate exported transformation XML with a smoke-load before promoting it
When it happens
Trigger: During XML deserialization of step metadata: the configured class path '<kettle-home>/plugins/steps/ScriptValues_mod/<jar>' does not exist or is unreadable, the JAR is corrupt, the class name is wrong/missing, Class.newInstance() fails (no public no-arg ctor, or constructor throws), or the XML structure is malformed.
Common situations: Transformation XML moved to a machine where the extra JAR was not deployed under plugins/steps/ScriptValues_mod/; typo in the additional class name; the additional class was compiled against a different JDK or its no-arg constructor changed; plugin folder renamed.
Related errors
- ColumnExistsMeta.Exception.UnableToReadStepInfo
- CreditCardValidatorMeta.Exception.UnableToReadStepInfo
- CubeOutputMeta.Exception.UnableToLoadStepInfo
- DelayMeta.Exception.UnableToReadStepInfo
- DynamicSQLRowMeta.Exception.UnableToLoadStepInfo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/08226d1e6462f378.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/steps/scriptvalues_mod/ScriptValuesMetaMod.java:990
Document dom = XMLHandler.loadXMLFile( DefaultBowl.getInstance(),
strActPath + "/plugins/steps/ScriptValues_mod/plugin.xml" );
Node stepnode = dom.getDocumentElement();
Node libraries = XMLHandler.getSubNode( stepnode, "js_libraries" );
int nbOfLibs = XMLHandler.countNodes( libraries, "js_lib" );
additionalClasses = new ScriptValuesAddClasses[nbOfLibs];
for ( int i = 0; i < nbOfLibs; i++ ) {
Node fnode = XMLHandler.getSubNodeByNr( libraries, "js_lib", i );
String strJarName = XMLHandler.getTagAttribute( fnode, "name" );
String strClassName = XMLHandler.getTagAttribute( fnode, "classname" );
String strJSName = XMLHandler.getTagAttribute( fnode, "js_name" );
Class<?> addClass =
LoadAdditionalClass( strActPath + "/plugins/steps/ScriptValues_mod/" + strJarName, strClassName );
Object addObject = addClass.newInstance();
additionalClasses[i] = new ScriptValuesAddClasses( addClass, addObject, strJSName );
}
} catch ( Exception e ) {
throw new KettleException( BaseMessages.getString(
PKG, "ScriptValuesMetaMod.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, "ScriptValuesMetaMod.Exception.UnableToLoadAdditionalClass" ), e );
}View on GitHub (pinned to f3058517a1)