pentaho/pentaho-kettle · critical · KettlePluginException
No ID specified for plugin with class
Error message
No ID specified for plugin with class: ${className} What it means
BasePluginType.handlePluginAnnotation processes a plugin annotation discovered during plugin scanning. Before registering, it calls extractID(annotation); if the annotation yields no ID (empty or null), Kettle cannot key the plugin in the registry, so it throws KettlePluginException naming the annotated class. This indicates the plugin's annotation is missing its required id/idattribute value.
Solutions
- Set the id attribute on the plugin annotation, e.g. change @Step() to @Step(id = "MyStep", name = "MyStep", ...).
- Rebuild the plugin jar so the compiled annotation actually contains the id (stale jars may lack it).
- If using a custom plugin type, verify the idAttribute passed to the BasePluginType constructor matches an actual annotation element name.
- Check for duplicate/conflicting annotation jars on the plugin classpath and remove stale versions.
Example fix
// before
@Step(name = "MyStep", description = "Does things", category = "Transform")
public class MyStepMeta extends BaseStepMeta { ... }
// after
@Step(id = "MyStep", name = "MyStep", description = "Does things", category = "Transform")
public class MyStepMeta extends BaseStepMeta { ... } Defensive patterns
Strategy: validation
Validate before calling
// Before scanning/registration, validate each annotated plugin class
Class<?> clazz = MyStepMeta.class;
Step ann = clazz.getAnnotation(Step.class);
if (ann == null || ann.id() == null || ann.id().isEmpty()) {
throw new IllegalStateException("Plugin annotation missing id: " + clazz.getName());
} Type guard
static boolean hasPluginId(Class<?> clazz) {
for (java.lang.annotation.Annotation a : clazz.getAnnotations()) {
try {
Object id = a.getClass().getMethod("id").invoke(a);
if (id instanceof String && !((String) id).isEmpty()) return true;
} catch (Exception ignored) { }
}
return false;
} Try / catch
try {
pluginType.handlePluginAnnotation(clazz, annotation, libraries, nativePlugin, pluginFolder);
} catch (KettlePluginException e) {
if (e.getMessage().contains("No ID specified")) {
log.error("Fix annotation id for " + clazz.getName());
}
} Prevention
- Always set the id attribute on plugin annotations (@Step(id=...), @JobEntry(id=...)).
- Add a unit test asserting every annotated plugin class hasPluginId().
- Keep custom plugin types' idAttribute aligned with the annotation element name.
- Rebuild and redeploy jars after annotation changes to avoid stale class files.
When it happens
Trigger: Calling handlePluginAnnotation (directly or via a PluginType's searchForPlugins/annotation scan) on a class whose @Step/@JobEntry/etc. annotation has an empty or null id field, or whose extractID returns empty because the configured idAttribute is not set on the annotation.
Common situations: A plugin author forgets to set the id attribute in @Step(id=...) or similar; a custom plugin type defines an idAttribute that doesn't match the annotation's field name; annotation processing picks up a class annotated with an older annotation version lacking the id element.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Unable to load native plugins
- AccessInput.Exception.CouldnotFindField
- AccessInput.Log.NoField
- AddSequence.Exception.NoSpecifiedMethod
- At this time we don't support the use of multiple cluster…
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/2b23704f087ddc67.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/core/plugins/BasePluginType.java:890
* @param clazz
* The class to use
* @param annotation
* The annotation to get information from
* @param libraries
* The libraries to add
* @param nativePluginType
* Is this a native plugin?
* @param pluginFolder
* The plugin folder to use
* @throws KettlePluginException
*/
@Override
public void handlePluginAnnotation( Class<?> clazz, java.lang.annotation.Annotation annotation,
List<String> libraries, boolean nativePluginType, URL pluginFolder ) throws KettlePluginException {
String idList = extractID( annotation );
if ( Utils.isEmpty( idList ) ) {
throw new KettlePluginException( "No ID specified for plugin with class: " + clazz.getName() );
}
// Only one ID for now
String[] ids = idList.split( "," );
String packageName = extractI18nPackageName( annotation );
String altPackageName = clazz.getPackage().getName();
String pluginName = getTranslation( extractName( annotation ), packageName, altPackageName, clazz );
String description = getTranslation( extractDesc( annotation ), packageName, altPackageName, clazz );
String category = extractCategory( annotation );
//deprecation is either based on a deprecated annotation or a category that contains "deprecated" in the translation key
boolean deprecated = extractDeprecated( annotation ) ||
( !Utils.isEmpty( category ) ? category.toLowerCase().contains( "deprecated" ) : false );
category = getTranslation( category, packageName, altPackageName, clazz );
String imageFile = extractImageFile( annotation );View on GitHub (pinned to f3058517a1)